Meteor template gets rendered twice - meteor

My template is getting rendered twice on first load. I notice this because in
Template.home.rendered = function() {
console.log('rendered'); // => this is printed out twice
console.log(Data.find({}).count()); // => this is initially 0, then 1 the second time
}
Furthermore, on the first load, no Data is available. Yet on the second load, the Data is there.
Does anyone know what this problem might be, and why the data only appears the second time?

You need to find a way to render the template when your data is available.
Using this template structure, the if block content, which happens to be the template displaying your data, will be rendered only when the myDataIsReady helper returns true. (thus triggering the rendered callback only once, with data immediately available).
<template name="displayData">
<p>This is my data : {{this}}</p>
</template>
<template name="home">
{{#if myDataIsReady}}
{{#each data}}
{{> displayData}}
{{/each}}
{{/if}}
</template>
You have to define a subscription handle (an object returned by Meteor.subscribe) in order to use it's reactive ready method : we'll reference it in the myDataIsReady helper to track data availability, and the helper will automatically rerun when the state of ready changes.
Meteor.startup(function(){
// this subscription should return your data subset
myDataHandle=Meteor.subscribe("myData");
});
Template.home.myDataIsReady=function(){
return myDataHandle.ready();
}
Template.home.data=function(){
return Data.find({});
}
But that's quite annoying for such a simple task.
That's why I suggest using the Iron Router which makes things way simpler !
Add it to your project using "mrt add iron-router", then in a client/router.js and client/router.html, use this boilerplate code :
Router.configure({
loadingTemplate:"loading"
});
Router.map(function(){
this.route("home",{
path:"/",
// we indicate which subscription has to be marked ready in order to load the template
waitOn:function(){
return Meteor.subscribe("myData");
}
// the result of this function will become our target template data context
data:function(){
return Data.find({});
}
});
});
<template name="home">
{{#each this}}
{{> displayData}}
{{/each}}
</template>
<template name="loading">
<p>Data isn't ready yet...</p>
</template>
As you can see, the Iron Router allows us to specify simply what we painfully achieved manually in the first code example (waiting on a particular subscription to render a template), and of course we get free routing, loading mechanisme, layout management, etc...
Search the web for a complete iron-router tutorial (my code is untested, but I hope it is ok and should get you started), it's so awesome that it's gonna be merged to Meteor ultimately.

I had a body.html in /client and a appBody.html in /client/templates, with, in iron router:
Router.configure({
layoutTemplate: 'appBody',
});
Both body templates were rendered (and happened to be the same). Obviously, the body.html in /client needed to be removed.

Related

How to access the subscriptions inside the HTML file?

Usually when I use helpers, I can access the returned values as below:
Template.oveview.helpers({
item: function () {
return Requests.find({});
},
Then in the client side I can use {{#each item}}, But I don't know how to display them in the .html when using publish and subscribe
Here is my publish:
Meteor.startup(() => {
Meteor.publish('requests', function queryRequests() {
return Requests.find({});
});
});
And here is my subscribe:
Template.overview.onCreated(function() {
Meteor.subscibe('requests');
});
How can I display the returned value from publish in the client side?
You can use it in a few different ways. You can use Meteor templates to insert a HTML snippet for every item in-between existing HTML:
{{#each item}}
{{> htmlTemplateName}}
{{/each}}
Or you can just place raw HTML in the {{#each}} loop:
{{#each item}}
<p>{{propertyX}}</p>
<p>{{propertyY}}</p>
{{/each}}
You might run into problems with the pubsub, depending on load order (I don't know load orders, I'm afraid). I used the iron-router package in my project, to bind certain end-points to specific HTML files. iron-router has this nice parameter you can set for every page, called waitOn, where I placed my subscriptions. This means that subscribing to a certain collection happens before anything else.
Router.configure({
layoutTemplate: '_layoutTemplate',
name: 'myTemplateName',
waitOn: function() {
return [
Meteor.subscribe('requests'),
//Add other subscriptions here
];
}
});

Meteor not displaying data from collection in html

I am trying to get data from a collection in meteor and using a helper passing it to a template.
Here is my code in collection:
Meteor.publish('displayCustomers', function tasksPublication() {
return Customers.find();
});
Below code in template JS file
Template.customerlist.onCreated(function() {
Meteor.subscribe('displayCustomers');
});
Template.customerlist.helpers({
displayCustomers :function(){
console.log(Customers.find({}));
return Customers.find({});
},
});
Template:
<template name="customerlist">
<h1>All registered users</h1>
{{#each displayCustomers}}
{{fname}}
{{/each}}
</template>
It is only displaying HTML content i.e. <h1>All registered users</h1>
Check that your publication is returning values to the client with this MiniMongo chrome extension
Check to make sure Customers is defined on the server and that your publish block is running only on the server.
Also I would toss a debugger into your onCreated block to make sure that your subscribe is being initialized.
Other than that, your code looks fine. I would try installing MeteorToys Mongol for client pub/sub debugging. https://atmospherejs.com/msavin/mongol
You need to actually fetch documents in your template :
Template.customerlist.helpers({
displayCustomers :function(){
return Customers.find().fetch(); //will return an array with all published documents
},
});

Controlling JS load order in Meteor with nested templates breaks with template.rendered?

I've got this template named indexmade up of a bunch of partial templates.
<template name="index">
{{> jumbotron }}
{{> crew }}
{{> projects3 }}
{{> projects2 }}
{{> faq }}
{{> contact }}
</template>
Say that I've got JS code page-transitions.js that requires the DOM of jumbotron to be fully loaded in order to run.
It's not enough to simple do:
Template.index.rendered = function(){
// load page-transitions.js
};
I HAVE to do
Template.jumbotron.rendered = function(){
// load page-transitions.js here instead
};
This can get messy really quick because you need to be VERY specific about which partial templates need which JS code. And different templates could require the same JS code so you can run into a situation where you're loading the same JS code multiple times.
Is there a way to wait until the index template has completely rendered EVERYTHING, including all nested child templates, and then run the JS code?
Meteor.startup() doesn't work in this case either.
You can try a couple of things:
First, just use the classic jQuery.ready way. because, why not?
Meteor.startup(function() {
$(document).ready(function() {
// stuff.
});
});
Or you could try Tracker.afterFlush:
Meteor.startup(function() {
// I know each Template.template.rendered has its own afterFlush, but those
// computations are defined way before Meteor.startup so then, theoretically,
// the below code should be executed last even if it triggers recomputations here.
Tracker.afterFlush(function() {
// stuff.
});
});

Can I pass the this._id value from one template helper to another with Meteor?

I have the following templates (.html) with their respected managers (.js files):
adminManageCategories
adminAddCategory
adminUpdateCategory
Consider the following:
<template name="adminManageCategories">
{{#each category}}
<div class="clickme">{{title}}</div>
{{/each}}
{{> adminUpdateCategory}}
</template>
Notice the {{> adminUpdateCategory}} is outside of the iteration. This is also a form, and I want to keep it on the same page.
And admin_manage_categories.js
Template.adminManageCategories.events({
"click .clickme": function(event) {
event.preventDefault();
console.log(this._id);
}
});
Notice the console.log() function, which works, as the template manager is smart enough to know the ID of the item that was clicked.
What I want to do is load this items values into the form when clicked. My example above is slim, but in my real data I have a title, sort order, among other things.
So my question is, what is the proper way to pass the _id from the adminManageCategories template to the adminUpdateCategory template, which is the form?
I can hack at this with JavaScript and make things happen, but I think I'm missing a "Meteor way" of doing things.
You need to use a ReactiveVar to store the currently clicked item.
First you need to run meteor add reactive-var, as it's not a package added by default in a standard meteor web app.
JS:
Template.adminManageCategories.created=function(){
// instantiate the reactive-var in the created callback
// we store it as a property of the template instance
this.currentItemId=new ReactiveVar(null);
};
Template.adminManageCategories.helpers({
// this helper reactively returns the currently clicked item
currentItem:function(){
// retrieve the reactive-var from the template instance...
var currentItemId=Template.instance().currentItemId.get();
// ...to fetch the correct collection document
return Items.findOne(currentItemId);
}
});
Template.adminManageCategories.events({
"click .clickme": function(event,template) {
event.preventDefault();
// assign the correct item id to the reactive-var attached to this template instance
template.currentItemId.set(this._id);
}
});
HTML:
<template name="adminManageCategories">
{{#each category}}
<div class="clickme">{{title}}</div>
{{/each}}
<p>Current item title is : {{currentItem.title}}</p>
{{! pass the currentItem as a parameter to your child template this will be
accessible as {{item}} in the HTML and "this.item" in JS helpers or
"this.data.item" in created/rendered/destroyed callbacks}}
{{> adminUpdateCategory item=currentItem}}
</template>
EDIT:
When I initialize the reactive-var in the created callback, I set it to null, this means that until one item is clicked, the helper will return null too and when you'll try to access this.item._id in the adminUpdateCategory this will fail.
The simplest way to solve this issue is maybe to not initialize the variable to null but to the first item in the collection.
Template.adminManageCategories.created=function(){
var firstItem=Items.findOne({},{
sort:{
sortedField:1
}
});
this.currentItemId=new ReactiveVar(firstItem && firstItem._id);
};
There may still be a case when you have 0 items in the collection, so you'll probably end up having to guard against the existence of the item in the JS.
Template.adminUpdateCategory.helpers({
itemProperty:function(){
return this.item && this.item.property;
}
});

Setting {reactive: false} when searching a collection in Meteor still updates the template

I've got a news feed idea I'm trying out in Meteor, but I'm having issues with making the damn thing behave :) I want it to load up the news feed on page load / refresh, but not when the data changes. I found in the documentation that adding {reactive: false} to the find method of a collection should make it stick to the results generated on render, but it doesn't seem to work for me. Meteor keeps updating the template right away.
Here's the code I've got:
On the server side:
Meteor.publish("newsfeed", function () {
return Newsfeed.find({});
});
On the client side:
Meteor.subscribe('newsfeed');
Template.feed.feed_data = function() {
var feed = Newsfeed.find({}, {
sort: {updated_time: -1},
limit: 10,
reactive: false
});
return feed;
};
In the template:
<template name="feed">
<div id="feed-wrapper">
<ul>
{{#each feed_data}}
<li>
<div class="message">{{message}}</div>
</li>
{{/each}}
</ul>
</div>
</template>
If I then run Newsfeed.update({_id: 'some_random_id'}, {$set: {date_created: 'some_random_date'}}) in Dev Tools, the template updates the sorting of my news feed and changes it.
How do I make it not do that? :D
This is arguably a bug in Meteor. Passing reactive: false means that minimongo itself doesn't set up some code to say "observe and if it changes, invalidate". But #each has its own separate observeChanges call which uses the observe callbacks directly (not a reactive "invalidate and recalculate") to update the list. Probably we should not do this if the cursor has reactive: false on it. Track this in https://github.com/meteor/meteor/issues/771 !
Thats a bit odd, it ought to work. You could also use preserve:
Try adding this line in your client js
Template.feed.preserve(['#feed-wrapper']);
Btw is the template name="feed" in another template? Does this template have any reactive variables in it?

Resources