How to wait to render view in backbone.js until fetch is complete?

JavascriptAsynchronousbackbone.js

Javascript Problem Overview


I'm trying to understand how a portion of backbone.js works. I have to fetch a collection of models once the app begins. I need to wait until fetch is complete to render each view. I'm not 100% sure the best approach to take in this instance.

var AppRouter = Backbone.Router.extend({
    routes: {
        "": "home",
        "customer/:id": "customer" 
    },

    home: function () {
        console.log("Home");
    },

    customer: function (id) {
        if (this.custromers == null)
            this.init();

        var customer = this.customers.at(2); //This is undefined until fetch is complete. Log always says undefined.
        console.log(customer);
    },

    init: function () {
        console.log("init");
        this.customers = new CustomerCollection();
        this.customers.fetch({
            success: function () {
                console.log("success"); 
                // I need to be able to render view on success.
            }
        });
        console.log(this.customers);
    }
});    

Javascript Solutions


Solution 1 - Javascript

The method I use is the jQuery complete callback like this:

var self = this;
this.model.fetch().done(function(){
  self.render();
});

This was recommended in a Backbone bug report. Although the bug report recommends using complete, that callback method has since been deprecated in favor of done.

Solution 2 - Javascript

You can also do this with jquery 1.5+

$.when(something1.fetch(), something2.fetch()...all your fetches).then(function() {
   initialize your views here
});

Solution 3 - Javascript

You can send your own options.success to the collections fetch method which runs only when the fetch is complete


EDIT (super late!)

From the backbone source (starting line 624 in 0.9.1)

fetch: function(options) {
  options = options ? _.clone(options) : {};
  if (options.parse === undefined) options.parse = true;
  var collection = this;
  var success = options.success;
  options.success = function(resp, status, xhr) {
    collection[options.add ? 'add' : 'reset'](collection.parse(resp, xhr), options);
    if (success) success(collection, resp);
  };

Note the second to last line. If you've passed in a function in the options object as the success key it will call it after the collection has been parsed into models and added to the collection.

So, if you do:

this.collection.fetch({success: this.do_something});

(assuming the initialize method is binding this.do_something to this...), it will call that method AFTER the whole shebang, allowing you trigger actions to occur immediately following fetch/parse/attach

Solution 4 - Javascript

Another useful way might be to bootstrap in the data directly on page load. This if from the
FAQ:

Loading Bootstrapped Models

When your app first loads, it's common to have a set of initial models that you know you're going to need, in order to render the page. Instead of firing an extra AJAX request to fetch them, a nicer pattern is to have their data already bootstrapped into the page. You can then use reset to populate your collections with the initial data. At DocumentCloud, in the ERB template for the workspace, we do something along these lines:

<script>
  var Accounts = new Backbone.Collection;
  Accounts.reset(<%= @accounts.to_json %>);
  var Projects = new Backbone.Collection;
  Projects.reset(<%= @projects.to_json(:collaborators => true) %>);
</script>

Solution 5 - Javascript

Another option is to add the following inside of your collections initialize method:

this.listenTo(this.collection, 'change add remove update', this.render);

This will fire off the render method whenever the fetch is complete and/or the collection is updated programmatically.

Solution 6 - Javascript

You Can Use on and Off Methods

if you want to add trigger method like suppose if you want on success you want to call render method so please follow below example.

    _this.companyList.on("reset", _this.render, _this);
    _this.companyList.fetchCompanyList({firstIndex: 1, maxResult: 10},     _this.options);

in Model js please use like

    fetchCompanyList: function(data, options) {
    UIUtils.showWait();
    var collection = this;
    var condition = "firstIndex=" + data.firstIndex + "&maxResult=" + data.maxResult;
    if (notBlank(options)) {
        if (notBlank(options.status)) {
            condition += "&status=" + options.status;
        }
    }
    $.ajax({
        url: "webservices/company/list?" + condition,
        type: 'GET',
        dataType: 'json',
        success: function(objModel, response) {
            UIUtils.hideWait();
            collection.reset(objModel);
            if (notBlank(options) && notBlank(options.triggerEvent)) {
                _this.trigger(options.triggerEvent, _this);
            } 
        }
    });
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionFrankieView Question on Stackoverflow
Solution 1 - JavascriptRovanionView Answer on Stackoverflow
Solution 2 - JavascriptiririrView Answer on Stackoverflow
Solution 3 - JavascripttkoneView Answer on Stackoverflow
Solution 4 - JavascriptMartinView Answer on Stackoverflow
Solution 5 - JavascriptIcedDanteView Answer on Stackoverflow
Solution 6 - JavascriptSaraf SissddharthView Answer on Stackoverflow