Pergunta

Is there any sort of hooks in backbone where I can easily say "whenever any of the collections is fetching data, show the spinner, hide it when they're done"?

I have a feeling it will be more complicated than that and require overwriting specific functions. When should I show the spinner? On fetch() or refresh() or something else?

Foi útil?

Solução

Backbone doesn't trigger any event when Collection::fetch() starts (see source code), so you will have to override the fetch method. Maybe something like this:

var oldCollectionFetch = Backbone.Collection.prototype.fetch;

Backbone.Collection.prototype.fetch = function(options) {
    this.trigger("fetch:started");
    oldCollectionFetch.call(this, options);
}

This will override the fetch method to give you an event when the fetch starts. However, this only triggers the event on the specific collection instance so if you have a bunch of different collections you'll have to listen for that event on each collection.

Outras dicas

You can use jQuery ajaxStart and ajaxStop. Those will globally run when an ajax request is made, so fetch and save will cause those to run. Add your code to show the spinner in the start and hide it in the end.

in Backbone.js 1.0.0 you can use the request and sync events http://backbonejs.org/#Events-catalog This goes in the view.

    initialize: function(){
        this.items = new APP.Collections.itemCollection();
        this.items.bind('request', this.ajaxStart, this);
        this.items.bind('sync', this.ajaxComplete, this);
    }

    ajaxStart: function(arg1,arg2,arg3){
        //start spinner
        $('#item-loading').fadeIn({duration:100});
    },
    ajaxComplete: function(){
        $('#item-loading').fadeOut({duration:100});
    }

This can be applied per collection or per model here's some CSS for the spinner http://abandon.ie/notebook/simple-loading-spinner-for-backbonejs

The way i have done this without overriding backbone is:

In view

var myView = Backbone.View.extend({
  initialize; function(){

    this.$el.addClass('loading');
    collection.fetch(success:function(){
      this.$el.removeClass('loading')
    })
  }
})

The other route would be to remove the loading class when the models are added, usually you have:

var myView = Backbone.View.extend({
  initialize; function(){
    _.bindAll(this, 'addAll')
    collection.bind('reset', this.addAll)

    this.$el.addClass('loading');
    collection.fetch();
  },
  addAll: function(){
    this.$el.removeClass('loading');
    collection.each(this.addOne);
  }
})

These would be almost identical in most cases, and as the loader is really for the users experience removing it just prior to displaying the content makes sense.

And a little update. Since Dec. 13, 2012 have been added a "request" event to Backbone.sync, which triggers whenever a request begins to be made to the server. As well since Jan. 30, 2012 have been added a "sync" event, which triggers whenever a model's state has been successfully synced with the server (create, save, destroy).

So, you don't need to override or extand the native Backbone's methodes. For listening 'start/finish fetching' event you can add listener to your model/collection like this for example:

var view = new Backbone.View.extend({
    initialize: function() {
        this.listenTo(this.model, 'request', this.yourCallback); //start fetching
        this.listenTo(this.model, 'sync', this.yourCallback); //finish fetching
    }
});

You can create a method called sync on any of your models, and backbone.js will call that in order to sync. Or you can simply replace the method Backbone.sync. This will allow you to make the change in only one place in your source code.

I have used NProgress in my backbone and it is the best functioning loader/spinner out there.

var view = Backbone.View.extend({
     initialize: function () {
          this.items = new APP.Collections.itemCollection();
          this.items.on('reset', this.myAddFunction, this);
          NProgress.start();
          collection.fetch({
               reset:true,
               success: function () {
                   NProgress.done(true);
               }
          });
      }
});

Use Backbone sync method, It will call every time backbone sync method, not only fetch, save, update and delete also

/* over riding of sync application every request come hear except direct ajax */

Backbone._sync = Backbone.sync;
Backbone.sync = function(method, model, options) {
    // Clone the all options
    var params = _.clone(options);

params.success = function(model) {
    // Write code to hide the loading symbol
     //$("#loading").hide();
    if (options.success)
        options.success(model);
};
params.failure = function(model) {
    // Write code to hide the loading symbol
     //$("#loading").hide();
    if (options.failure)
        options.failure(model);
};
params.error = function(xhr, errText) {
    // Write code to hide the loading symbol
     //$("#loading").hide();
    if (options.error)
        options.error(xhr, errText);
};
// Write code to show the loading symbol
     //$("#loading").show();
Backbone._sync(method, model, params);
};
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top