简体   繁体   中英

Preferred pattern for master-detail scenario in Backbone.js

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:

  • Create/Destroy views for each active detail. (This seems like excess work, not to mention potential zombie problems )
  • Maintain an additional model that represents the active detail and create one view that re-renders itself when the model updates
  • Create one view with a method that swaps out the associated model (I think this can lead to event binding problems and is probably not a good idea)

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM