@jameslaneconkling wrote:
Trying to find best practices for a component relationship I see a lot in my apps: cousin components that are backed by the same models, where there is a one-to-many relationship between the shared ancestor component and each cousin. E.g.
ancestor-component | +-----------+------------+ v v parent1 parent2 + + +----------------+ +----------------+ v v v v v v cousinA cousinB cousinC cousinA cousinB cousinC
Each set of siblings represent a different visualization of the data. Cousins A are backed by the same data, cousins B likewise, etc. I'm looking for best practices in ways to sync the visual state of cousins. E.g. hover on one cousin, and the effect is registered on it's corresponding cousin.
I've found two general patterns: two-way binding using the model, and unidirectional using the data-down, actions-up pattern. E.g., to have corresponding cousin components enter a hover state when either is hovered on:
// register an isHoverState on the model export default Ember.Component.extend({ classNames: ['child-one'], classNameBindings: ['model.isHoverState:hover'], announceHoverState: Ember.observer('model.isHoverState', function() { console.log('cousins hovered:', this.get('model.id')); }), mouseEnter() { this.set('model.isHoverState', true); }, mouseLeave() { this.set('model.isHoverState', false); } });
// send mouseEnter/Leave actions up to the shared ancestor component, // and a childOne/TwoHover property down to the other parent component // (see the twiddle for all the code) export default Ember.Component.extend({ classNames: ['child-two'], classNameBindings: ['cousinIsHover:hover', 'selfIsHover:hover'], cousinIsHover: Ember.computed('childOneHover', function() { return this.get('childOneHover') === this.get('model'); }), announceCousinHover: Ember.observer('childOneHover', function() { if (this.get('childOneHover') === this.get('model')) { console.log('cousins hovered:', this.get('model.id')); } }), mouseEnter() { this.sendAction('mouseEntered', this.get('model')); this.set('selfIsHover', true); }, mouseLeave() { this.sendAction('mouseLeft', this.get('model')); this.set('selfIsHover', false); } });
I have some early observations about the pros and cons of each approach, but I wanted to know if anyone who has spent more time in the Ember space (I'm going on two months here), has any input on best practices, antipatterns, or other approaches I didn't consider here.
Initial thoughts:
two-way binding via model
- takes ~1/2 the lines of code, and is simpler in some ways to reason about
- requires you to store view state on the model (bad?)
- it's not possible to tell which cousin component set the state (bad?)
unidirectional flow
- requires a lot more code, particularly with a deep component hierarchy from ancestor to cousin
- the component signatures become a lot longer as the number of possible states increases (b/c you are passing down more data, and up more actions)
- I don't like having to pass the
childOneHover
model down to allchild-two
components (andchildTwoHover
model to allchild-one
components), and compare it against it's own model to make sure it's a match.- it is possible to tell which cousin initiated the state change, and thus manage reactions to state change in a much more detailed way. (very good)
Any thoughts/feedback? Hope this isn't too general (it's very specific to a number of cases I'm working on, but maybe not for others?)
Posts: 2
Participants: 2