The majority of Backbone tutorials that deal with master-detail scenarios handle the detail state by creating new views with models passed from routes ( example ). I'm wondering what the alternative options are what the pros and cons are for each.
The thing that has always stood out to me about this approach of instantiating new views is that it seems like a missed opportunity to make use of Backbone's model-view binding. Ie, create a single detail view that updates based on the selected model. But this would require a separate model concerned only with the UI / active state.
I can think of three different patterns:
In the context of a simple case like the example tutorial linked above, what are the pros/cons? Is the idea in #2 of using a UI model a bad practice? Are there other solutions?
In my experience, UI models have been incredibly helpful. We maintain a ContextModel
for every "page" in our app. What makes it really nice is that we guarantee "pages" to be initialized with a complete ContextModel
so there is only ever one place the context is originally created.
Our AppView
always handles creating the initial context for a page when a specific route is triggered. The route handler would be where the defaults for a context are set, and things are parsed from the route's values.
Once the page itself has been initialized, it only has to worry about changes to the ContextModel
and have handlers that do any updates necessary and then update the path.
Here's an example pulled directly from our AppView
:
onSomeRoute : function (project, category, pivot, tab, item) {
// Setup page context with defaults.
var options = {
category : 'traits',
tab : 'population',
item : null
};
// Figure out what the allowed categories and tabs are from this
// environment by checking against all schema items.
[snipped]
// Resolve the `category` and the `pivot`. Try and match the pivot's
// slug, else choose the first pivot in the category.
if (_(categories).contains(category)) options.category = category;
[snipped]
if (pivots) options.pivot = pivots.find({ slug : pivot }) || pivots.first();
// Resolve the `tab` and `item`. Only allow valid tabs, and then try
// to match the item's slug as well, or choose the first one.
var tabs = ['population', 'sources', 'engagement', 'retention'];
if (_(tabs).contains(tab)) options.tab = tab;
[snipped]
// Now that we've applied some defaults, make sure the path is
// updated to reflect what the interface is showing.
[snipped]
this.loadPage('some-page', options);
}
And then in the PageView
in question we have a couple change handlers:
// When category changes, try to match the current pivot against pivots
// in the category, else choose the first pivot from the category.
onCategoryChange : function (context, category) {
var schema = this.context.get('environment').get(category);
if (!schema.contains(this.context.get('pivot')))
this.context.set('pivot', schema.first());
this.router.update({
category : category
});
},
// When the pivot changes, create a new set of segments for the context.
onPivotChange : function (context, pivot) {
Segmenter.segment(context, function (segments) {
context.get('segments').reset(segments).each(function (segment) {
segment.evaluate();
});
});
this.router.update({
pivot : pivot.get('slug')
});
},
Notice that all of the change handlers keep the URL for the page updated based on the changes in the context. (We wrote a couple custom methods on the router to make this really easy for us.) But they also do any other logic we need to have happen for the page.
Hopefully some of that helps give you ideas. In general, any state that is stored in the URL is in our PageContext
and then a couple other things that are derived from that state are also stored there for convenience.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.