Show loading indicator on partial template - meteor

I have the following template
<template name="myTabs">
<div class="my-tabs">
<div class="page-header">
<h1>My Tabs</h1>
</div>
<form class="navbar-form navbar-left" role="search">
New
<div class="form-group">
<input type="text"
name="tab"
class="form-control typeahead"
autocomplete="off" spellcheck="off"
data-source="tabs"
data-template="tabSearch"
placeholder="Filter"/>
</div>
</form>
{{>tabsTable}}
</div>
</template>
And the following nested template
<template name='tabsTable'>
<table class="table table-hover">
... table that displays data from collection
</table>
</template>
And the following route
this.route('myTabs', {
path: '/mytabs',
layoutTemplate: 'dashboardLayout',
loginRequired: 'entrySignIn',
waitOn: function() {
return this.subscribe("mytabs", Meteor.userId());
},
data: {
tabs: Tabs.find({})
},
onAfterAction: function() {
SEO.set({
title: 'My Tabs | ' + SEO.settings.title
});
}
});
Problem is that when i visit the myTabs route, the whole template is replaced by the loading template, instead of just the part that depends on the collection being loaded, namely the tabsTable template.
How can I make it so that the loading template is only displayed on the nested template, so that all the html that doesnt depend on the collection being loaded is still shown (title, wrapping html etc..)?

Instead of using waitOn with a loadingTemplate in the router, you can use subscriptions which will give you a ready calllback which you can use to hide the loading template in your view.
Router.route('myTabs', {
path: '/mytabs',
layoutTemplate: 'dashboardLayout',
loginRequired: 'entrySignIn',
subscriptions: function() {
this.subs = Meteor.subscribe("mytabs", Meteor.userId());
},
data: function() {
return {
tabs: Tabs.find(),
ready: this.subs.ready
};
},
onAfterAction: function() {
SEO.set({
title: 'My Tabs | ' + SEO.settings.title
});
}
});
Then in your view
<template name="myTabs">
<div class="my-tabs">
<!-- ... -->
{{#unless ready}}
{{> loadingTemplate}}
{{else}}
{{> tabsTable}}
{{/unless}}
</div>
</template>

Related

How can I route my messenger and messages in Meteor?

Im creating a an instant messenger app and im having a little trouble routing it. So, once you go into the app. There is a list of available users. You can click on a user and start chatting. The issue I have is once I click send, the console show an Uncaught TypeError: Cannot read property 'value' of undefined. Im not sure what im doing wrong here. Also I need help show the chat messages sent above. As if you can see the recent and previous messages. Here are my codes. Any examples and helps would be great.
HTML
minstant
<body>
</body>
<!-- this is the main template used by iron:router to build the page -->
<template name="ApplicationLayout">
{{> yield "header"}}
<div class="container">
{{> yield "main"}}
</div>
</template>
<!-- top level template for the nav bar -->
<template name="navbar">
<nav class="navbar navbar-default">
<div class="container-fluid">
<div class="navbar-header">
<a class="navbar-brand" href="/">
Minstant!
</a>
</div>
<div class="nav navbar-nav">
{{> loginButtons}}
</div>
</div>
</nav>
</template>
<!-- Top level template for the lobby page -->
<template name="lobby_page">
{{> available_user_list}}
</template>
<!-- display a list of users -->
<template name="available_user_list">
<h2>Choose someone to chat with:</h2>
<div class="row">
{{#each users}}
{{> available_user}}
{{/each}}
</div>
</template>
<!-- display an individual user -->
<template name="available_user">
<div class="col-md-2">
<div class="user_avatar">
{{#if isMyUser _id}}
<div class="user_avatar">{{getUsername _id}} (YOU)
<br/>
<img src="/{{profile.avatar}}" class="avatar_img">
</div>
{{else}}
<a href="/chat/{{_id}}">
{{getUsername _id}}
<br/>
<img src="/{{profile.avatar}}" class="avatar_img">
</a>
{{/if}}
</div>
</div>
</template>
<!-- Top level template for the chat page -->
<template name="chat_page">
<h2>Type in the box below to send a message!</h2>
<div class="row">
<div class="col-md-12">
<div class="well well-lg">
{{#each recentMessages}}
{{> message}}
{{/each}}
</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<form class="new-message">
<input class="input" type="text" name="chat" placeholder="type a message here...">
<button class="btn btn-default">Send</button>
</form>
</div>
</div>
</template>
<!-- simple template that displays a message -->
<template name="message">
<div class = "container">
<div class = "row">
<div class = "username">{{username}}
<img src="/{{profile.avatar}}" class="avatar_img">
</div>
<div class = "message-text"> said: {{messageText}}</div>
</div>
</div>
</template>
Here is my JS
Messages = new Mongo.Collection("messages");
if (Meteor.isClient) {
Meteor.subscribe("messages");
Meteor.subscribe("userStatus");
// set up the main template the the router will use to build pages
Router.configure({
layoutTemplate: 'ApplicationLayout'
});
// specify the top level route, the page users see when they arrive at the site
Router.route('/', function () {
console.log("rendering root /");
this.render("navbar", {to:"header"});
this.render("lobby_page", {to:"main"});
});
// specify a route that allows the current user to chat to another users
Router.route('/chat/:_id', function () {
this.render("navbar", {to:"header"});
this.render("chat_page", {to:"main"});
});
///
// helper functions
///
Template.available_user_list.helpers({
users:function(){
return Meteor.users.find();
}
})
Template.available_user.helpers({
getUsername:function(userId){
user = Meteor.users.findOne({_id:userId});
return user.profile.username;
},
isMyUser:function(userId){
if (userId == Meteor.userId()){
return true;
}
else {
return false;
}
}
})
Template.chat_page.helpers({
recentMessages: function () {
return Messages.find({}, {sort: {createdAt: 1}});
return Meteor.users.find();
},
});
Template.chat_page.events({
// this event fires when the user sends a message on the chat page
'submit .new-message':function(event){
event.preventDefault();
var text= event.target.text.value;
// stop the form from triggering a page reload
event.target.text.value = "";
// see if we can find a chat object in the database
// to which we'll add the message
Meteor.call("SendMessage", text);
},
});
};
Meteor.methods({
sendMessage: function (messageText) {
if (! Meteor.userId()) {
throw new Meteor.Error("not-authorized");
}
Messages.insert({
messageText: messageText,
createdAt: new Date(),
username: Meteor.user().username
});
}
});
// start up script that creates some users for testing
// users have the username 'user1#test.com' .. 'user8#test.com'
// and the password test123
if (Meteor.isServer) {
Meteor.startup(function () {
if (!Meteor.users.findOne()){
for (var i=1;i<9;i++){
var email = "user"+i+"#test.com";
var username = "user"+i;
var avatar = "ava"+i+".png"
console.log("creating a user with password 'test123' and username/ email: "+email);
Meteor.users.insert({profile:{username:username, avatar:avatar}, emails: [{address:email}],services:{ password:{"bcrypt" : "$2a$10$I3erQ084OiyILTv8ybtQ4ON6wusgPbMZ6.P33zzSDei.BbDL.Q4EO"}}});
}
}
},
),
Meteor.publish("messages", function () {
return Messages.find();
});
Meteor.publish("userStatus", function() {
return Meteor.users.find({ "status.online": true });
});
};
The error is with your form submit code. In the console you can see the error is in Template.chat_page.events.submit .new-message on line 73. That will take you right to the error code:
'submit .new-message':function(event){
event.preventDefault();
var text = event.target.text.value;
// stop the form from triggering a page reload
event.target.text.value = "";
}
event.target.text.value doesn't exist. event.target does, but there is no field for text. There is textContent, and you can access the children of the target (which in this case is the form). Put in a console.log(event); and figure out what you are trying to access within the javascript console and then use that to determine what your code should look like. This might work for you:
'submit .new-message':function(event){
event.preventDefault();
var text = event.target.elements.chat.value;
// stop the form from triggering a page reload
event.target.text.value = "";
}
event.target.elements.chat.value comes from the name field of the <input>.

Uploaded Images is being deleted when meteor js restarts

I'm still new to Meteor. I'm creating a Meteor JS project and one of the central features of it is image upload or more like a featured image upload. The upload works fine and after clicking on submit it will show on my <div class="viewing">. The problem occurs when you restart Meteor. The image will be deleted and thus no image is displayed by the time I run the program.
Here's my code:
The HTML:
<body>
{{> viewPost}}
<h1>Welcome to Meteor!</h1>
{{> addPost}}
</body>
<template name="addPost">
<form class="main-form">
<input type="text" name="title"/>
<br><br>
<input type="file" name="imgUp" class="fileIn"/>
<br><br>
<button>Submit</button>
</form>
</template>
<template name="viewPost">
{{#each images}}
<div class="viewing">
<img src="{{this.url store='images'}}"/>
</div>
{{/each}}
</template>
JS:
Images = new FS.Collection("images", {
stores: [new FS.Store.FileSystem("images",{path: "~/uploads"})],
filter: {
allow: {
contentTypes: ['image/*'] //allow only images in this FS.Collection
}
}
});
if (Meteor.isClient) {
Template.addPost.events({
'submit .main-form': function(event, template){
var title = event.target.title.value;
event.preventDefault();
var fileObj = template.find('input:file');
Images.insert(fileObj.files[0], function (err,fileObj){});
}
});
Template.viewPost.helpers({
images: function(){
return Images.find({});
}
});
}
Please tell me what I am doing wrong here. Any help would be great! I've been looking for a way to solve this for a few days already.

Why does my template not reactive?

If I first open http://localhost:3000/, then click the test link, the roles labels will be displayed.
But if I directly open http://localhost:3000/test(Input the url in Chrome's address bar and hit enter), the roles labels will not be displayed.
Here is my code:
In client startup I subscribe to something:
Meteor.publish("Roles", function(){
return Roles.find();
});
Meteor.startup(function() {
if(Meteor.isClient) {
Meteor.subscribe('Roles');
}
});
And roles template:
Template.roles.helper( {
allRoles: function() {
return Roles.find();
}
})
html
<template name="roles">
<div>
{{#each allRoles}}
<label>test label</label>
{{/each}}
</div>
</template>
The problem is sometime roles template is rendered before the Roles is ready.
So these is no role labels displayed.
But according to Meteor document, helpers is a reactive computation, and Database queries on Collections is reactive data source. So after Roles is ready, the {{#with allRoles}} is reactive and should be displayed.
Why does roles not be displayed?
And then I rewrite my code to:
Meteor.startup(function() {
if(Meteor.isClient) {
roles_sub = Meteor.subscribe('Roles');
}
});
Template.roles.helper( {
allRoles: function() {
console.log(2);
return Roles.find();
},
isReady: function() {
console.log(1);
console.log(roles_sub.ready());
return roles_sub.ready();
}
})
html
<template name="roles">
<div>
{{#if isReady}}
{{#each allRoles}}
<label>test label</label>
{{/each}}
{{/if}}
</div>
</template>
And still role labels cannot be displayed.
And console gives me:
1
false
1
false
1
true
2
Which means isReady() is reactive? but why my roles labels remains blank?
Can somebody explain this?
use Template.subscriptionsReady
server/publish.js
Meteor.publish("Roles", function(){
return Roles.find();
});
client/client.js
Meteor.startup(function() {
Meteor.subscribe('Roles');
});
Template.roles.helpers({ // --> .helper change to .helpers
allRoles: function() {
return Roles.find();
}
})
client/templates.html
<template name="roles">
<div>
{{# if Template.subscriptionsReady }}
{{#with allRoles}}
<label>{{> role }}</label>
{{/with}}
{{ else }}
loading....
{{/if}}
</div>
</template>
<template name="role">
<div>{{ _id }}</div>
</template>
A reactive function that returns true when all of the subscriptions
https://github.com/meteor/meteor/blob/9fe2f4b442ec84eac5352b476d014c977c5ae4a2/packages/blaze/template.js#L424

Dynamic templates and routing in Meteor.js

I am developing a meteor.js application. I have two main templates/pages, home.html and groups.html. I render these templates according to the selection of main menu. In home template, I just show basic informations about the website. But in groups template, I have side menu which has timeline, chat, group blog options, and according to the selection of side menu, I render timeline or chat or groupblog templates inside groups template. I succeeded this with two different methods.
Can you please tell me if any of them is correct approach, and if they both are, which one is better to use?
In the first approach, is there any way to wait to render sub-template until data is ready, like waitOn property in iron-router?
First approach: I am using dynamic template, and rendering and setting data contex of sub-templates by using Session variable.
layout.html
<template name="layout">
<div>
{{> header}}
<div id="main" class="container">
{{> yield}}
</div>
</div>
</template>
groups.html
<template name="groups">
<div class="row">
<div class="row-fluid">
<div class="col-md-9 col-lg-9">
{{> dynamictemplate}}
</div>
</div>
</div>
</template>
dynamictemplate.html
<template name="dynamictemplate">
{{#DynamicTemplate template=getTemplate data=getData}}
Default content when there is no template.
{{/DynamicTemplate}}
</template>
dynamictemplate.js
Template.dynamictemplate.helpers({
getData: function () {
var tempname="timeline";
if(Session.get("menuitemselected")!=null){
tempname =Session.get("menuitemselected");
}
if(tempname=="timeline"){
return {test:123};
}else if(tempname=="calendar"){
return {groupId:this._id};
}
},
getTemplate: function () {
var tempname="timeline";
if(Session.get("menuitemselected")!=null){
tempname =Session.get("menuitemselected");
}
return tempname;
}
});
Second approach, I am defining new routes for all sub-templates in routes, and I am using yield with region property in home template.(I do not know if i am supposed to use yield in any place other than layout.html)
layout.html
<template name="layout">
<div>
{{> header}}
<div id="main" class="container">
{{> yield}}
</div>
</div>
</template>
groups.html
<template name="groups">
<div>
<div class="col-md-3">
{{> sidemenu}}
</div>
<div id="main" class="col-md-9">
{{> yield region="detailrender"}}
</div>
</div>
</template>
router.js
.....
this.route('groupdetail', {
path: '/groupsmain/:_id',
layoutTemplate: 'layout',
yieldTemplates: {
'timeline': {to: 'detailrender'}
},
data: function() { return Groups.findOne(this.params._id);
}
});
this.route('groupdetailcalendar', {
path: '/groupsmain/:_id/calendar',
layoutTemplate: 'layout',
template: 'groupdetail',
yieldTemplates: {
'calendar': {to: 'detailrender'}
},
data: function() { return Groups.findOne(this.params._id);
.....
});

Reactive-Tables Meteor

I am trying to get reactive-tables to work but I am having no luck following the instructions on GitHub.
This is what I have:
In my Main.html:
{{> courseTable }}
in my course_table.html:
<template name="courseTable">
<div id="table">
{{ > reactiveTable collection=Courses}}
</div>
</template>
in courses.js:(works with autoForm)
Courses = new Meteor.Collection("courses", {
schema: {....
Is there something I am missing? From my understanding once these commands are used, the rest is done from within the package. I can't find any more information on this package anywhere.
What I have now just shows a blank screen.
Thanks in advance!
This is what I have: (I'm using Meteor framework and bootstrap-3 package)
in index.html
<template name="clientes">
<div class="container">
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title">Clientes</h3>
</div>
<div class="panel-body">
{{> reactiveTable collection=tables settings=tableSettings}}
</div>
</div>
</div>
</template>
in index.js
var Clientes = new Meteor.Collection('clientes')
Template.clientes.tables = function () {
return Clientes;
}
Template.clientes.tableSettings = function () {
return {
rowsPerPage: 10,
showFilter: false,
showNavigation: 'auto',
fields: [
{ key: 'nombre', label: 'Nombre' },
{ key: 'apellido', label: 'Apellido' },
{ key: 'correoe', label: 'E-mail' }
],
useFontAwesome: true,
group: 'client'
};
}
With this I can display all the records in the collection.
I hope this help you to go any further.
Courses is the collection object. To get some courses, you need to query the courses with find:
Courses.find()
However, to make this accessible in the template, you need a helper function.
//course_table.js
Template.courseTable.helpers({
courses: function () {
return Courses.find()
}
});
Then you can can set the table collection using the helper method (I used a lowercase "courses" for the helper method for clarity):
{{> reactiveTable collection=courses}}

Resources