2

I have a model which describes an organization, and a sub-collection which describes permissions on the organization.

In my view I need to fetch data from both the models before rendering. When I bind my view rendering method to one event (e.g model.change or subcollection.sync), there are times which my screen displays before my models are populated, thus producing an error.

Here is the relevant part of my code:

  this.model = new OrgModel({id: id});
  this.model.permissions = new PermissionsCollection({org: id});

  this.model.permissions.on("sync", this.render);  // SOMETIMES GETS CALLED BEFORE model:sync GETS CALLED

  this.model.permissions.fetch({error: Utils.apiError});
  this.model.fetch({error: Utils.apiError});

I am unsure which event to call "render" - because there are times which each request comes back first.

Is there a way to bind the "sync" events together so that when a model.fetch is called, it in turn calls the subcollection.fetch, and does not pass back the "sync" event until the subcollection:sync is called?

Scott Switzer
  • 1,064
  • 1
  • 15
  • 25
  • I guess I could make the fetches serial, like so: this.model.permissions.fetch({ success: function() { this.model.fetch({error: Utils.apiError})}, error: Utils.apiError }); Not sure if this is the best way – Scott Switzer Feb 22 '13 at 13:51
  • 1
    [Backbone - performing multiple fetch() before rendering a view](http://stackoverflow.com/a/14963041/557612) – steveax Feb 22 '13 at 15:44

3 Answers3

3
var deferred1, deferred2, that = this;
this.model = new OrgModel({id: id});
this.model.permissions = new PermissionsCollection({org: id});
deferred1 = this.model.permissions.fetch({error: Utils.apiError});
deferred2 = this.model.fetch({error: Utils.apiError});

$.when(deferred1, deferred2).then(function(){
    that.render();
});

There is a very powerful concept of deferreds and promises. More on the topic can be read on api.jquery.com/jQuery.Deferred

Raman Shalupau
  • 171
  • 2
  • 9
0

It's probably not the best way to do it, but I usually handle by:

  • Adding a success function after fetching your model of collection. Each function would initialise a variable like this.permissionsFetched = true;
  • Modifying render() so that your view, or part of it, is rendered if your this.permissionsFetched and this.modelFetched variables have been initialised.

This way, your first render won't trigger an error, and your view should be rendered after your second fetch result, regardless of the order they executed...

It doesn't feel 100% clean and there might be a better way to do this. However it's working OK for me and it's a bit better than making your fetches serial.

AntonyG
  • 861
  • 9
  • 22
  • Thanks - however this would push my race condition down to the render (e.g. if the main portion of my page has not rendered but the subview that uses the collection is ready, this will lead to rendering issues) – Scott Switzer Feb 22 '13 at 15:50
  • well, not necessarily. I wasn't clear in my answer, read instead: Modifying render() so that your view is only rendered if your `this.permissionsFetched` and `this.modelFetched` variables have been initialised. The first fetch result will trigger you render() but the view won't be rendered because the condition wouldn't be met yet. – AntonyG Feb 23 '13 at 14:32
  • Alternatively, you could also use default values to create an "empty" model to allow the view to be rendered ([http://backbonejs.org/#Model-defaults](http://backbonejs.org/#Model-defaults)) with no model attributes, and then updated later when the model is fetched. – AntonyG Feb 23 '13 at 14:38
0

The way I handle this is by using a counter to track how many requests are currently pending:

  • On request the counter is incremented
  • On sync or error the counter is decremented
  • When the counter goes from 0 to 1, I fire a loading event
  • When the counter goes from 1 to 0, I fire a loaded event

You could implement a system like this and bind your render function to the loaded event.

Ben
  • 10,056
  • 5
  • 41
  • 42