1

I am trying to understand Backbone and tried to create some small REST API fronted, but can not get View to work.

fetch() returns valid JSON array of three elements. And this.collection.models is not empty (typeof object - []) - it has tree child object elements in it. But each iteration doesn't fire.

When check if collection.models exists with console.log(this.collection.models); it looks like all is right:

console.log(this.collection.models)

I would be thankful for any advice!

var Account = Backbone.Model.extend({});

var AccountsCollection = Backbone.Collection.extend({
    model: Account,
    url: 'api/v1/accounts',
    initialize: function () {
        this.fetch();
    }
});

var AccountsView = Backbone.View.extend({
    render: function() {

        // Check if there is something
        console.log(this.collection.models);

        // This doesn't work
        _.each(this.collection.models, function(model) {
            console.log(model);
        });

        // Neither this
        this.collection.each(function(model) {
            console.log(model);
        });
    }
});


var a = new AccountsView({collection: new AccountsCollection()});
a.render();
Orbitum
  • 1,585
  • 5
  • 27
  • 47
  • 2
    What makes you think that the AJAX `fetch` call has gotten anything from the server yet? Why don't you bind to some events on the collection instead of assuming anything about the timing of an AJAX call? – mu is too short Jun 15 '13 at 17:34
  • I added image from console output as argument why I think AJAX call has got something. I don't know what is best approach - if I do something wrong - just let me know. – Orbitum Jun 15 '13 at 17:54
  • @Orbitum That's not what he meant. The problem is that you launch the AJAX request in `initialize` and immediately attempt to use the results in `render`. Those results aren't available yet, you need to wait for them to arrive before rendering. – Mattias Buelens Jun 15 '13 at 18:00
  • Ok, I understand. How can I made it to wait till results is available and only after exit from initialize function? Thank you! – Orbitum Jun 15 '13 at 18:08
  • And careful with trusting `console.log`, that grabs a reference which can change between when you call `console.log` and when it gets around to putting things in the console. If you're going to use `console.log` with async things, you'll want to log a copy of the data at the instance in time that matters (i.e. `console.log(some_collection.toJSON())` or similar trickery). I have some notes on this issue over here: http://stackoverflow.com/a/11463190/479863 – mu is too short Jun 15 '13 at 18:14
  • @Orbitum: I just updated my comment with a link to a relevant old answer of mine, it might be useful to you. – mu is too short Jun 15 '13 at 18:17

1 Answers1

4

First option

var AccountsCollection = Backbone.Collection.extend({
    model: Account,
    url: 'api/v1/accounts'
});

var AccountsView = Backbone.View.extend({
    render: function() { /**/ }
});

var collection = new AccountsCollection();
var view = new AccountsView({collection:collection});

collection.fetch().done(function () {
    // collection has been fetched
    view.render();
});

Second option

var AccountsCollection = Backbone.Collection.extend({
    model: Account,
    url: 'api/v1/accounts'
});

var AccountsView = Backbone.View.extend({
    initialize: function() {
        this.listenTo(this.collection, 'sync', this.render);
    },
    render: function() { /**/ }
});

var collection = new AccountsCollection();
var view = new AccountsView({collection:collection});

Third option

var AccountsCollection = Backbone.Collection.extend({
    model: Account,
    url: 'api/v1/accounts',
    initialize: function () {
        this.deferred = this.fetch();
    }
});

var AccountsView = Backbone.View.extend({
    initialize: function() {
        this.collection.deferred.done(_.bind(this.render, this));
    },
    render: function() { /**/ }
});

var collection = new AccountsCollection();
var view = new AccountsView({collection:collection});
Vitalii Petrychuk
  • 14,035
  • 8
  • 51
  • 55
  • Yes, it works! But I have question - you moved fetch function outside `AccountsCollection` and now it is not so "modular". If I will extend this small code with more REST collections/models I don't think it will look good. Maybe there are more "modular" solution? Thank you! – Orbitum Jun 15 '13 at 18:13
  • @Orbitum: Bind your view's `render` to the appropriate events from the collection. – mu is too short Jun 15 '13 at 18:15
  • @Orbitum: http://backbonejs.org/#Events-catalog and http://backbonejs.org/#Collection-fetch – mu is too short Jun 15 '13 at 18:19
  • @Orbitum In this case inside the collection `initialize` method save link to the result of the `fetch` method - `this.deferred = this.fetch()`. Then in the view `initialize` method - `this.collection.deferred.done(_.bind(this.render, this))` – Vitalii Petrychuk Jun 15 '13 at 18:21
  • 1
    @Orbitum Another one option - view `initialize` method: `this.listenTo(this.collection, 'sync', this.render)` – Vitalii Petrychuk Jun 15 '13 at 18:24
  • Yes, this works too. Now I must search which method to use. Thank you! – Orbitum Jun 15 '13 at 18:27