Tri Nguyen
Tri Nguyen

Reputation: 11138

Meteor template gets rendered twice

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?

Upvotes: 1

Views: 1868

Answers (2)

MastaBaba
MastaBaba

Reputation: 1154

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.

Upvotes: 0

saimeunt
saimeunt

Reputation: 22696

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.

Upvotes: 2

Related Questions