Jeanluca Scaljeri
Jeanluca Scaljeri

Reputation: 29179

Meteor: load collection in 'rendered' function

In my Meteor app I have this rendered function which needs to access a specific collection. Assume I have a collection ToDos

ToDos = new Meteor.Collection('todos');

if (Meteor.isClient) {
    Meteor.subscribe('todos');
}

if (Meteor.isServer) {
    Meteor.startup(function () {
        Meteor.publish('todos', function () {
            return ToDos.find({});
        });
    });
}

Now in a rendered function I want to do something like this

Template.todos.rendered = function () {
    var todo = ToDos.findOne({...});

    if (todo) {
        $('[datepicker]').datepicker('setDate', todo.date);
    }
}

The problem I have is that when rendered is called, todo is undefined and this function is only called once. Is there a fix for this ? Can Deps.autorun help ?. If yes, how ?

On the other hand, if I could just listen for the ToDos only I could update the datepicker there!

Upvotes: 1

Views: 696

Answers (2)

hharnisc
hharnisc

Reputation: 927

Assuming you've got a simple app with only one page

A possible implementation with deps

if (Meteor.isClient) {
    Meteor.startup(function() {
        Meteor.subscribe('todos');
        Deps.autorun(function(e) {
            var todo = ToDos.findOne({...});
            var $picker = $('[datepicker]');
            if (!!todo && !!$picker) {
                $picker.datepicker('setDate', todo.date);
            }
        });
    });
}

Meteor renders the template first and then grabs data. So when the data changes you have to go and set your data picker.

Collection.observe might be of interest to you too. You can get a callback every time data is added, changed, deleted, etc.

var todo = ToDos.find({...});
var handle = todo.observeChanges({
    added: function (id, todo) {
        // do stuff on added
    },
    removed: function () {
        // do stuff on removed
    }
});

Upvotes: 1

richsilv
richsilv

Reputation: 8013

Yes, this is a potentially annoying problem in Meteor. Essentially, whilst the handle that your Meteor.subscribe call returns has a ready method which will tell you if the collection is ready or not, usually your rendered callbacks will have run by the time it returns true, so it's not very useful in this case. You can use the onReady callback in your subscribe call in some cases, and it's even possible to plug into a template's data context from elsewhere if you know the template instance exists and do anything you could in the rendered callback, but this would probably be a very messy way of dealing with the problem you present (because if there was ever a case in which the collection was ready before the DOM had been rendered it would fall down).

SOLUTION: As is often the case, iron router is the best solution here, as it allows each route to have a waitOn callback, which you can use to specify which subscriptions need to be ready() before the page begins to render (although you can wait on anything with a ready callback, not just subscriptions). That way, you always know that your Collection.find will return what you're expecting when your rendered callbacks first run.

Upvotes: 1

Related Questions