jQuery Deferred and Backbone JS

by on

Backbone is a really interesting framework, and my favorite part so far is the following idea:

If you use a callback, you're Doing It Wrong

This has held true for me for my development with Backbone so far. When you make data calls to the server, you let the appropriate events notify interested parties when objects are changed or updated or refreshed.

However, with non-data operations, callbacks can be really useful. Today, I needed to animate an object, using jQuery's slideUp. I wanted the slideUp to go hand-in-hand with a deletion of an object. Because slideUp is asynchronous, and the deletion action is asynchronous, I needed a callback to synchronize them. The reason I couldn't do them simultaneously is that when an object is deleted, many view elements refresh themselves, and if the slideUp wasn't finish the dom refreshing would interrupt the slideUp and it looked gross.

So, I need a callback on slideUp to call remove. Here arose an SRP problem: the view should not concern itself with the removal of the model from the collection. One solution is for the view's removal method to take a callback and pass it along to slideUp. But I wanted something more flexible. Enter jQuery Deferred.

Deferred objects let you chain callbacks and return promises as objects. For you Rails readers, imagine AREL had a hot sister written in Javascript that was into AJAX.

So here is my view method that runs slideUp and returns a deferred object:


return $(this.el).slideUp(200).promise().done(
_.bind(function() { this.remove(); }, this
);

Now when we archive and email, we want to remove the dom element by sliding, then we want to tell the email model to archive itself. Here is the archive method:


this.remove().then(this.model.archive);

Pretty straightforward. We call remove (the previous method) then we tell the model to archive itself. This method is bound to the click event on the archive button using Backbone's view event binding methods. When the model archives itself, Backbone events automatically fire, so other Views can listen for "change" and "remove" to update their elements.

EDIT: changed code reflecting Julian's comments.

Comments

Julian
Using jQuery 1.6, you can considerably simplify your method:

remove: function() {
return
$(this.el)
.slideUp(200)
.promise()
.done(function() { this.remove(); });
}

1.6 adds jQuery.fn.promise() that returns a Promise to observe when a collection has no more animation going on. It's resolved with the collection is was called on as its context and first & only argument.

(sorry for how the code is formatted, Blogger's comments just plain suck :/)
Nick Gauthier
Cool! Yeah I noticed slideUp didn't return a promise, so I didn't know what to do with it.

I think I'd still need the _.bind within the done to bind to the object though, right?
blog comments powered by Disqus