Reputation: 1181
I have this issue with a BackboneJS, where I have a collection that is being fetched. The collection fetch triggers fetch on all the models in it, but the model's fetch (or initialize, take your pick) is a custom jQuery call along the lines of
fetch: function(options) {
$.getJSON('/my/uri/').success(function(data) {
processData(data);
});
}
The problem is that the collection event hooks render my data before all the models in the collection have been loaded and processed (as the getJSON is asynchronous). I can see how I would do this with a single model; I found some references to using deferred variables. What's the suggested approach here?
Upvotes: 0
Views: 887
Reputation: 2780
I would first recommend that you use Backbone's api for your XHR requests. It will make it more consistent as well as keeping "fetch" native to Backbone. Just a suggestion...
To your question. You have a couple options here. Using Backbone's sync api:
Example Model: Use "parse" as your "processData" call.
var MyModel = Backone.Model.extend({
url: '/my/uri/',
//http://backbonejs.org/#Model-parse
parse: function(response) {
// this is your 'processData' call. Do your logic here. This is done before the consumer gets the data.
// return the correct object.
return response.Data;
}
})
// Usage Example: Do what you need to do in success.
var myModel = new MyModel();
myModel.fetch()
.success(function(data){
// this is the data AFTER parse has done it's magic. Do other view logic.
});
Here is an example of using it in a view and subscribing the "sync" event on the model. NOTE You have to trigger fetch on the model.
var MyView = Backbone.View.extend({
template: _.template(someHTMLTemplate),
initialize: function() {
// setup a sync event that will render once the model fetch is complete.
this.model = new MyModel()
.on('sync', this.render, this);
// this is just an example and I don't recommend putting fetch in initialize.
this.model.fetch();
},
render: function() {
this.$('.content').html(this.template({
model: this.model.toJSON()
}));
}
});
I would avoid custom triggers as much as possible as they are hard to maintain and are easy to get out of hand on large teams if everyone is not consistent in the naming convention and usage!
Finally, if you have a couple events that the view needs to do before it renders, then I would use the $.when method to keep track of the promise objects.
Upvotes: 0
Reputation: 3363
What you are doing in the fetch
is just loading a JSON file and passing it to somewhere else. this is not what fetch stands for.
if you'd like to fetch by yourself, you should set(reset
) data into your collection and trigger an event for the eventlistener.
fetch: function(options) {
$.getJSON('/my/uri/').success(function(data) {
_result = doSomethingForTheData(data);
//this makes reset event
yourCollection.reset(_result);
//or you can make your custom event whatever you want.
yourCollection.reset(_result,{silent : true});
yourcollection.trigger("yourCustomEventName",yourCollection);
});
}
But I think what you are trying to achieve doesn't require overriding of fetch
in your Collection
var YourCollection = Backbone.Collection.extend({
url: 'my/uri'
});
in your View
yourCollection = new YourCollection();
yourCollection.fetch()
yourView.listenTo(yourCollection,"sync",function(){
this.refresh()
//or whatever you want with your updated Collection.
});
You can also use success callback of AJAX to work on methods working asynchronously like you are trying to do.But There is no reason not to use Backbone loose coupling structure(event trigger/listen).
Upvotes: 1
Reputation: 2092
The Backbone-y way of doing this is to instead have the Model provide a "parse" function. That function receives the raw response from the server for further processing. I think whatever you need to do in processData(...)
could be accomplished there instead. Assuming you do that, you should be able to just trust Backbone to do the right thing to get the modified response into the model as attributes.
So your Model & Collection could look something like:
var MyModel = Backbone.Model.extend({
parse: function(response) {
// Do what you need to modify
var modified = modifyResponse(response);
return modified;
},
// ... whatever else you need
});
var MyCollection = Backbone.Collection.extend({
model: MyModel,
url: '/my/uri',
// ... whatever else you need
});
No need to override fetch(...)
and do anything too crazy. To get the data from the server it would be the same thing I assume you're already doing:
var collection = new MyCollection();
collection.fetch();
RE rendering before all the data has been returned, I suspect the View you're using is rendering on add
or change
events. If you want to wait for all the data to come back before rendering, register to listen for the sync
event instead, as that will be fired by the collection when all data has been received.
Upvotes: 0