0

I would like to render a view for a model when the model is first fetched but not on every change.

My setup is as follows:

var m = new $.model.Customer({id: customer});
var v = new $.view.GeneralEditView({el: $("#general"), model: m});
m.fetch();

Then in the view initialize I bind the change event to the render method to render when the model is loaded:

this.model.bind('change', this.render);

The problem is that the view then renders on every change. I'd like to only render after the fetch. Unfortunately I'm not aware of any event that's fired after a fetch for a model other than change.

Is there something like 'reset' for collections that I can bind to?

EDIT:

Perhaps to put it more succinctly, for Backbone models is there a way to distinguish when a model is loaded from the server versus changed locally?

Conor Power
  • 686
  • 1
  • 6
  • 17

3 Answers3

1

Models

You can make the change event specific to a certain key changing, such as the uniqueId (if you have one):

this.model.bind('change:id', this.render, this);

By default, fetch does not fire any event directly, but indirectly fires the change event once new data is loaded using set

If that is not an option, you can always trigger an event in your fetch function:

initialize: function () {
    this.model.bind("fetch", this.update, this);
}

fetch: function () {
    // do stuff
    this.model.trigger("fetch", this);
}

update: function () {
    // your refresh stuff here
}
Austin
  • 6,026
  • 2
  • 24
  • 24
  • thanks Austin. the question is about models not collections. I'm aware of the reset for collections and looking for something similar for models or a way of doing it other than change. – Conor Power Aug 20 '12 at 13:42
  • Then the second section detailing how to do this with models should suit your needs... – Austin Aug 20 '12 at 14:11
  • but I really want to know when the fetch is complete, rather than started as I want to render the view when the fetch is complete. – Conor Power Aug 20 '12 at 14:21
  • Then you put the `this.model.trigger` in the callback of your AJAX call – Austin Aug 20 '12 at 14:33
1

There are a bunch of different ways to approach this (these all assume var view = this; somewhere in your view code):

  • Call .fetch() with a one-time success callback:

    m.fetch({
        success: function() {
            view.render();
        }
    });
    
  • Bind to change but unbind in the handler:

    function handle() {
        view.render();
        view.model.off('change', handle);
    }
    this.model.bind('change', handle);
    
  • Use _.once to limit handler calls:

    this.model.bind('change', _.once(function() {
        view.render();
    }));
    
  • Use a .ready() pattern for your models - example here. I like this option in cases where multiple views need to know when the model is loaded, and when you need to want to be able to write the same code without worrying about whether your model is loaded yet. The downside of this is that it requires you to add a model method like .isFullyLoaded() to test every time; the upside is that using a test function, rather than setting a flag, allows models to be loaded in bulk as part of a collection without having to change your code.

nrabinowitz
  • 55,314
  • 10
  • 149
  • 165
  • I think this is the only reliable way to do it. The approach of using the new event synced doesn't guarantee that the model is properly populated after the sync has completed so it was causing issues. Opted for the "off" approach. thx – Conor Power Aug 20 '12 at 23:56
0

I may have a general solution from https://github.com/documentcloud/backbone/pull/1468#issuecomment-6766096. I overwrote the sync method on Backbone as follows:

Backbone.Model.prototype.sync = function(method, model, options) {

    var succ = options.success;
    var customSuccess = function(resp, status, xhr) {
         //call original
        succ(resp, status, xhr);
        model.trigger('synced', model, options);
    }
    options.success = customSuccess;
    Backbone.sync(method, model, options);
 }

To save the original success method as I don't want to mess that unless I need to, pass the custom success method. When the custom success method is invoked the custom event is triggered as suggested by @Austin and then the original success method in invoked.

Conor Power
  • 686
  • 1
  • 6
  • 17
  • would love to hear if anyone can foresee issues with this approach. I'm just slightly unsure how the scope of "this" might be impacted by all of this ... – Conor Power Aug 20 '12 at 15:06