Tabbed view state in backbone.js
I have an app that needs multiple top-level navigational tabs (e.g. Fruits & Vegetables). When clicked, the should have an id applied (e.g. #fruits). How would you approach this with backbone.js?
A couple ideas: (assume a function exists called body_id())
Per vie开发者_StackOverflow社区w route: when '#/fruits' is visited, call body_id('fruits'). (con: not DRY, need body_id() per route, e.g. '#/fruits/:id'
Option 1, but using a before filter (https://github.com/FLOChip/backbone_router_filter).
I can't help but think there's a better way. Suggestions?
Thanks!
I created a superclass for all the views which respond to the router. (I have other views, sub-views or widgets, if you will.) This superclass has all the smarts needed to hide/show the views that are routed, as well as decorate the container for all views with the tabs. All routed views inherit from that class. Both the routed views and the router "learn" about the IDs from a common pool, and are built on the fly using underscore's template
function, so the information is stored in a single place, preserving DRY.
Some views (views about collections, "fruits" or "vegetables") are permanent tabs, and some tabs (views about individuals items in a collection, i.e. "banana" or "cucumber") are temporary. The router has method calls to distinguish between #/:type
and #/:type/:id
, the latter of which are created as-needed. A variable in the router keeps track of which views have been generated already, and which need to be constructed, preserving memory by not constructing views if they're not asked for by the user, e.g.:
Controller.prototype.item = function(type, id) {
var v, view;
view = ((v = this.views)[type + '.' + id]) || (v[type + '.' + id] = (
new ItemView({model: this.collections.getByCid(type).getByCid(id)})).render());
view.show();
}
Note that in this idiom (which is rendered from Coffeescript), it's critical that your render
method return this;
. This allows new views to render themselves before being shown; existing views, presumably, have already been rendered and don't need re-rendering, just an elegant fade/slide/whatever in.
You could get especially pixel-perfect and have render() return a jQuery.deferred
object, and call .then(view.show)
, to make sure that the rendering was complete before showing.
精彩评论