17

I have two set of collections. One is for the categories and the other is for the Items. I ned to wait for the categories to finish fetching everything for me to set the category for the Items to be fetched.

Also i everytime i click a category i must re-fetch a new Items Collection because i have a pagination going on everytime i click on a category it doesn't refresh or re-fetch the collection so the pagination code is messing with the wrong collection. Any ideas?

this.categoryCollection = new CategoryCollection();
this.categoryCollection.fetch();

this.itemCollection = new ItemCollection();
this.itemCollection.fetch();
n0minal
  • 3,195
  • 9
  • 46
  • 71

6 Answers6

57

Just ran into a similar situation. I ended up passing jquery.ajax parameters to the fetch() call. You can make the first fetch synchronous. From the backbone docs:

jQuery.ajax options can also be passed directly as fetch options

Your code could be simplified to something like:

this.categoryCollection.fetch({async:false});
this.itemCollection.fetch();
santiagoIT
  • 9,411
  • 6
  • 46
  • 57
  • 6
    This isn't a good idea as it can cause the browser to lock up while everything waits for the synchronous call to finish. Straight from the documentation: "Note that synchronous requests may temporarily lock the browser, disabling any actions while the request is active. As of jQuery 1.8, the use of async: false with jqXHR ($.Deferred) is deprecated; you must use the success/error/complete callback options instead of the corresponding methods of the jqXHR object such as jqXHR.done() or the deprecated jqXHR.success()." [https://api.jquery.com/jQuery.ajax/] – richardaday Apr 03 '14 at 02:57
  • Use Async :false is not a good idea. it makes no sense in my opinion when you're working with SPA. – rcarvalho Jun 27 '14 at 19:54
  • This works great for me. I was trying to use fetch() with async and running the processes in series but fetch()'s success callback was firing before the sync was complete. – joshcanhelp Aug 25 '14 at 16:40
  • It should also be noted that this approach has been deprecated. See post: http://stackoverflow.com/questions/11448011/jquery-ajax-methods-async-option-deprecated-what-now – Josh Feb 19 '15 at 00:34
  • I have added a proper way of doing this http://stackoverflow.com/a/43427782/1735027 . – Nikola Loncar Apr 15 '17 at 15:24
15

One quick way would be to just pass a callback into the first fetch() call that invokes the second. fetch() takes an options object that supports a success (and error) callback.

var self = this;
this.categoryCollection = new CategoryCollection();
this.categoryCollection.fetch({
    success: function () {
        self.itemCollection = new ItemCollection();
        self.itemCollection.fetch();
    }
});

Not the most elegant, but it works. You could probably do some creative stuff with deferreds since fetch() returns the jQuery deferred that gets created by the $.ajax call that happens.

For the pagination issue, it's difficult to tell without seeing what your pagination code is doing. You're going to have to roll the pagination stuff yourself since Backbone doesn't support it natively. What I'd probably do is create a new Collection for the page criteria that are being queried and probably create a server action I could hit that would support the pagination (mapping the Collection's url to the paginated server action). I haven't put a ton of thought into that, though.

Rob Hruska
  • 118,520
  • 32
  • 167
  • 192
  • This is a not a way to do this making calls one after another!!! Here is only right way of doing that by using deferred objects : http://stackoverflow.com/a/43427782/1735027 – Nikola Loncar Apr 15 '17 at 15:26
2

I had to react to this thread because of the answers there.

This is ONLY WAY OF DOING THIS RIGHT!!!

this.categoryCollection = new CategoryCollection();
this.itemCollection = new ItemCollection();

var d1 = this.categoryCollection.fetch();
var d2 = this.itemCollection.fetch();

jQuery.when(d1, d2).done(function () {
     // moment when both collections are populated
     alert('YOUR COLLECTIONS ARE LOADED :)'); 
});

By doing that you are fetching both collections at same time and you can have event when both are ready. So you don't wait to finish loading first collections in order to fetch other, you are not making ajax calls sync etc that you can see in other answers!

Here is a doc on Deferred objects.

Note: in this example case when one or more deferred object fails it's not covered. If you want to cover that case also beside .done you will have to add .fail callback on .when and also add error handler that will mark failed d1 or d2 in this example.

Community
  • 1
  • 1
Nikola Loncar
  • 2,611
  • 1
  • 26
  • 38
0

I am using RelationalModel and I created a queued fetch, that only calls the 'change' event when done loading:

var MySuperClass = Backbone.RelationalModel.extend({
    //...

    _fetchQueue : [],

    fetchQueueingChange : function(name){
        //Add property to the queue
        this._fetchQueue.push(name);
        var me = this;

        //On fetch finished, remove it
        var oncomplete = function(model, response){
            //From _fetchQueue remove 'name'
            var i = me._fetchQueue.indexOf(name);
            me._fetchQueue.splice(i, 1);

            //If done, trigger change
            if (me._fetchQueue.length == 0){
                me.trigger('change');
            }
        };

        this.get(name).fetch({
            success: oncomplete, 
            error : oncomplete
        });
    },

    //...


});

The class would call:

this.fetchQueueingChange('categories');
this.fetchQueueingChange('items');

I hope you can improve on this, it worked well for me.

miguelr
  • 1,334
  • 14
  • 21
0

I ended up with the same problem today and figured out a solution to this:

var self = this;
this.collection = new WineCollection();
this.collection.url = ApiConfig.winetards.getWineList;
this.collection.on("reset", function(){self.render()});
this.collection.fetch({reset: true});

Now when the fetch on the collection is complete a "reset" is triggered and upon "reset" call the render() method for the view.

Using {async: false} is not the ideal way to deal with Backbone's fetch().

Ashwin
  • 1,942
  • 4
  • 30
  • 59
-10

just set jQuery to become synchronous

$.ajaxSetup({
  async: false
});   
this.categoryCollection.fetch();
this.itemCollection.fetch();
$.ajaxSetup({
  async: true
});

This is the simplest solution, I guess. Of course, starting new requests while these fetches run will be started as synchronous too, which might be something you don't like.

Akasha
  • 2,162
  • 1
  • 29
  • 47