I'm playing with firebase and backfire. The example doesn't explicitly say, but I've figured out that it loads data from the server when you instantiate an instance of your Backbone.Firebase.Collection
. E.g.:
var TodoList = Backbone.Collection.extend({
model: Todo,
firebase: new Backbone.Firebase("https://<your-namespace>.firebaseio.com")
});
var todos = new TodoList(); // fetches data
How do I know when the retrieval is done?
Having worked pretty extensively with Backfire's model, I have several thoughts on this. I hope some of them give you good ideas for your project.
Changing mental models to real-time environment
First of all, get out of the mentality of knowing when "all the data is loaded", assuming this troubles you as it did me early on. We're in a real-time environment now. Just start from zero and treat every record that comes in as an update. This saves a lot of time and energy trying to deal with states.
Lazy rendering and DOM bindings
Now with Backbone, I often find myself wanting to do a lazy render. That is, I want to deal with the following conditions logically:
A good solution for frequently changing data is Backbone.ModelBinder's CollectionBinder tool, which manipulates each node of the DOM individually instead of re-rendering all the records. There are plenty of examples on their site so I won't go into detail here.
Debounce as a quick and dirty solution
Underscore's debounce method is a great solution for smaller scale DOM manipulations that don't need all the complexity of data bindings. Debounce with a wait of about 250 works well for me, ensuring render always occurs on data changes, but only once if we get a large clump of updates in a row.
Assuming we have created a collection that extends Backbone.Firebase.Collection, then we can do something like the following:
var View = Backbone.View.extend({
initialize: function() {
this.listenTo( this.collection, 'add remove', _.debounce(_.bind(this.dataChanged, this), 250) );
},
render: function() {
this._isRendered = true;
/* do all my rendering magic here */
},
dataChanged: function() {
// wait until render has been called at least once
// if so, re-render on any change
this._isRendered && this.render();
}
});
Using a Deferred to wait for loaded data
On my implementation of Backfire, I've added a stateful method that notifies me on the first load. I did this using jQuery's Deferred object. Then I just listen for the collection to fire a sync
event:
this.collection.once('sync', /* data is loaded */);
A nice thing about Firebase is that the initial Firebase.on('child_added'...) results (the existing records) tend to come in one nice big clump--one after another. So as an added bonus, I use debounce to make my "loaded" method fire after the initial clump is completed, so I don't get one record, call loaded, and then immediately need to take some action for a series of updates.
Since this is implementation specific, I'm going to be a bit abstract here, but this is the gist of it:
// inside my wrapper for Backbone.Firebase.Collection
this.ready = $.Deferred();
// debounce our ready listener so it fires on the tail end of
// the initial update clump which reduces the number of update
// calls on the initial load process considerably
this.readyFn = _.debounce(this.ready.resolve, 250);
// start monitoring for the first series of updates
// this would need to be invoked before the sync is established
this.on( 'add', this.readyFn );
// wait for the data to come in
this.ready.always( _.bind(function() {
// when the ready promise is fulfilled, we can turn off the listener
this.off('add', this.readyFn);
// this is where we trigger the listener event used above
this.trigger('sync');
}, this) );
I'd use this solution with care. I find that in most cases I can simplify things greatly by forgetting about initial loads and initializing everything empty, then treating everything as an update.
I only utilize this in cases where I need to show some alternative view if no data exists (like instructions on getting started).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With