Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ember.js - where should interface state be stored?

Is there an official story for where interface state (as opposed to persisted model state) should live in an Ember.js app?

In the "Responding to User-initiated Events" part of the Router docs, there's an example of delegating click events to a photo's "showPhoto" method, but having a model "show" itself seems like an undesirable mixing of concerns.

I understand that in many cases state should be stored in the router so that the interface state is represented in the URL and is restored if you refresh the page or send the url to someone. But what about non-hierarchical state, such as the list of items selected on a page?

Ideally that type of state would be serialized as query/hash params (eg: http://www.hipmunk.com/flights/QSF-to-NYC#!dates=Sep15,Sep16p1;kind=flight&locations=QSF,YYZ&dates=Sep15,Sep23~tab=1 ) but as far as I know, the router doesn't offer that functionality, does it?

At BackboneConf, Jeremy Ashkenas said that the right way to do that in Backbone was to just store the state on the model too (he had an example of a model with a "selected" field). But I believe Tom Dale said he didn't think that was a good idea, and not how it should be done in Ember. Unfortunately I don't remember him mentioning how it should be done.

like image 396
Gabriel Grant Avatar asked Sep 14 '12 13:09

Gabriel Grant


People also ask

Is Ember better than react?

Ember is considered to be one of the best in distributor logic. It provides the best combination with ember-data and the best CLI, which makes working with Ember much easier. Almost all programmers love the default architecture provided by Ember. React JS does not provide any architecture for its own.

What is store in Ember JS?

The store contains all of the data for records loaded from the server. It is also responsible for creating instances of Model that wrap the individual data for a record, so that they can be bound to in your Handlebars templates.

What is route in Ember?

An Ember route is built with three parts: An entry in the Ember router ( /app/router. js ), which maps between our route name and a specific URI. A route handler file, which sets up what should happen when that route is loaded (app/routes/about.

How does Ember JS work?

Ember uses templates to organize the layout of HTML in an application. Ember templates use the syntax of Handlebars templates. Anything that is valid Handlebars syntax is valid Ember syntax. Here, {{name}} is a property provided by the template's context.


1 Answers

If you want state to be routable (i.e. reachable via a url), then it needs to be serializable and deserializable via ember's router. If state is transient and not routable, then it is probably best kept on the controller.

If you need to represent complex interface state across multiple models (say, for selecting items in a list), consider maintaining a controller-specific array of objects that wrap underlying data models. I think it's hackish to represent view state directly on models, especially if those models are used across multiple views.

For the example you provided, you might do something like this to hook up a complex route:

Ember.Route.extend({
  route: "flights/:cities/dates/:dates",

  serialize: function(router, context){
    return {cities: context.get('cities'),
            dates:  context.get('dates')};
  },

  deserialize: function(router, params){
    // return a context object that will be passed into connectOutlets()
    return {cities: params.cities,
            dates:  params.dates};
  },

  connectOutlets: function(router, context) {
    // pass the context from deserialize() in as the content of a FlightController
    router.get('applicationController').connectOutlet('flight', context);
  }
})

Note that you could also use a route such as "flights?cities=:cities&dates=:dates" but the above is probably cleaner and more SEO-friendly.


Expanded upon after Gabriel's comments: If you want to maintain an array of searches, each of which resides in its own tab, I'd recommend keeping the data for those searches in an application-level array (e.g. App.currentUser.activeSearches). My reasoning is that you don't want to have to recreate this data every time a user switches tabs. Instead, the router would retrieve this data in deserialize() and then pass it as the context to connectOutlets(). The view and controller to represent this data should be quickly re-constructed based upon this object when switching tabs. Let me extend my example from above:

Ember.Route.extend({
  route: "flights/:cities/dates/:dates",

  serialize: function(router, context){
    return {cities: context.get('cities'),
            dates:  context.get('dates')};
  },

  deserialize: function(router, params){
    // find or create a "Search" object that contains the filters and results,
    // which will be passed into connectOutlets()
    return App.currentUser.findOrCreateSearch({cities: params.cities,
                                               dates:  params.dates});
  },

  connectOutlets: function(router, context) {
    // pass the context (a search object) from deserialize() in as the content of a FlightController
    router.get('applicationController').connectOutlet('flight', context);
  }
})
like image 75
Dan Gebhardt Avatar answered Sep 25 '22 18:09

Dan Gebhardt