Reputation: 17779
Using Backbone.js, I have a collection that I instanctiate, X
. After instantiating it, I immediately called X.fetch()
such that the data can be loaded from the server as soon as possible. I then pass X
into various views that will operate on the collection.
What is the best way for these views do differentiate between when the collection is simply loading (and thus empty) and when it is loaded but actually empty? I would like my views to show appropriate "loading..." text up until the collection has pinged the server. At which point, I would like them to say "It looks like there's nothing here. Perhaps you should add something."
I was thinking of maybe listening to the reset
event on the collection in each respective view, but this seems rather fragile to me. What happens if the reset event has already fired before the view attaches its listener? Is there a good pattern to inspect the state of a collection to find out if it's been fetched?
Upvotes: 1
Views: 204
Reputation:
Make sure that you initialize the views before you call fetch()
. This way you can listen for the reset
event, and be sure that all views are updated. If you want the xlist to be loaded as soon as possible it could be a good idea to bootstrap it to your html server side.
A way of doing that could be:
<script src="/public/js/collecions/x-list.js">
<script src="/public/js/views/x-view.js">
<script>
var xlist = new App.Collections.XList();
_.each($(".xdiv"), function(el){
new App.Views.XView({
el: el,
collection: xlist
});
// make sure that XView have this in it's initialize method:
// this.collection.bind("reset", this.render, this)
}
xlist.fetch();
// or even better, use bootstraped data:
x.list.reset((<%= @data.to_json %>)
</script>
Upvotes: 1
Reputation: 7295
fetch
method is asynchronous based on asynchronous nature of AJAX.
Therefore you need to implement an event-driven behavior.
X.fetch({
success: function(collection, response) {
hidePreLoader();
renderViews(collection); // collection argument is your X collection.
}
});
Update
Based on your comments to my answer I can propose another idea. Namely you can set
X.isLoaded = true;
or any other property like
X.loadedAt = Date.now();
in success
callback, so other code can check for the state of this property.
But though I see a kind of bad design here.
You can render your views with preloader showed and in success
callback trigger some event on which your views will start to work with the collection since it's loaded and became ready to use.
So in total I'm again propose you to use event-driven behavior.
I didn't test, but here is a representation of my idea:
var XCollection = Backbone.Collection.extend({
// ...
model: X,
loadState: {},
loadedAt: -1,
initialize: function(options) {
_.extend(this.loadState, Backbone.Events);
},
isLoaded: function() {
return this.loadedAt > -1;
}
});
var Subview = Backbone.View.extend({
// ...
initialize: function(options) {
this.collection.loadState.on("loadComplete",
this.onLoadComplete, this);
},
onLoadComplete: function(response) {
this.hidePreloader();
this.renderData();
},
/**
* Checks is the collection loaded and view can render fetched models.
* It's just an example.
* You'll not need to use it if you're handling a loadComplete event.
*/
isRenderingAllowed: function() {
return this.collection.isLoaded();
}
});
var XView = Subview.extend({
// ...
initialize: function(options) {
Subview.prototype.initialize.apply(this, arguments);
}
});
var YView = Subview.extend({
// ...
initialize: function(options) {
Subview.prototype.initialize.apply(this, arguments);
}
});
// ...
var x = new XCollection();
var xView = new XView({collection: x}),
yView = new YView({collection: x});
x.fetch({
success: function(collection, response) {
collection.loadedAt = Date.now();
collection.loadState.trigger("loadComplete", response);
}
});
Upvotes: 3