Ember 2.0 has gone great lengths towards making everything a component. With routable components coming soon, controllers will probably phased out as well.
Context
However, there is a recurring problem I face when building User interface, which I don't have a satisfying pattern for so far: user interface state.
What am I taking about?
- Selection state
- Current focus
- Folded/unfolded state in some tree display
Basically, any state that is not part of the actual data, yet has to be tracked on an object-by-object basis. In the past, this used to be done with Controllers
, acting as proxies to models. This approach is now obsolete. The new approach is Components
everywhere, for the better. Component does the bookkeeping, tracks transient state and you get actions back.
A typical pattern I have, though, is with shared state, such as a list with selectable items.
The issue
Building a list component, with the following requirements:
- Each item can be selected.
- Selection state changes the DOM (some class bindings).
- User can draw a rectangle in the list component to select several items at once.
- Ideally, the whole behavior can be abstracted out as a mixin.
Question: where does the selection flag live?
Attempts
1) Everything is a component.
I make each item a sub-component, say {{my-list-item}}. The component tracks the selection state. Problem: how can the list component update the selection state?
2) Move state out of sub-components.
Put it on the list component. Within a separate state array alongside the item list. Pros: the list has all the state it needs. Cons: it's a nightmare to keep it synced when items are added or removed from the list. And it means the sub-component have no access to the state. Or maybe I could pass it to them as a bound value?
3) Reintroducing proxies.
Coming to think of it, there is a way to share some state: put it on the models. Well, not on the actual models so as not to pollute them with local state, but by setting up an ArrayProxy that will return some ObjectProxy
for every item, to hold the state.
Pros: this is the only solution I managed to implement completely. Cons: encapsulation and decapsulation of items is a hassle. Also, after a few layers of being passed around, get
and set
have to go through 4 ou 5 proxies, which I fear will be a problem with performance.
Also, it does not work well for mixins. Should I want to abstract out some HasSelection
mixin, and a HasFoldableItems
mixin, and a Sortable
mixin, they all need some state.
Back to the drawing board
Is there some better pattern I have not found?
I found the following relevant questions, but that led me nowhere:
- Ember.js - where should interface state be stored? (2012, suggests something close to my #3 above)
- Road to Ember 2.0 - High level Ember app structure feedback? (some of the key questions in the list are relevant to this one)