I have the following structure for my components:
{{#x-grandparent}}
{{#x-parent}}
{{x-child}}
{{x-child}}
{{x-child}}
{{/x-child}}
{{/x-grandparent}}
On every x-child component, I have a property that can be changed on user interaction. That property is only relevant for x-child components.
On x-grandparent, I have an action button that must read from every x-child the above property on click. Right now I’m using an event-bus service.
Another approach is to add that property (let say css) on x-grandparent and further down so the x-child can modify it.
{{#x-grandparent as |css|}}
{{#x-parent css=css as |css|}}
{{x-child css=css}}
{{x-child css=css}}
{{x-child css=css}}
{{/x-parent}}
{{/x-grandparent}}
None of the solutions above doesn’t feel the right way to do it. Do you have another approach for this?
We have this problem in our application from time to time. I haven’t found a solution that is always elegant. I see enough people asking about something similar to this that I hope a more elegant Ember solution will present itself.
That said, in this case I would have x-child
send an action up to x-grandparent
everytime state changes. Then have x-grandparent
cache that state so it’s ready when it needs to use it. Basically exactly what you said with CSS
, expect make it an action, instead of a bound value. I think this fits into the DDAU philosophy.
Example:
{{#x-grandparent as |cssChange|}}
{{#x-parent on-css-change=cssChange}}
{{x-child on-css-change=cssChange}}
{{x-child on-css-change=cssChange}}
{{x-child on-css-change=cssChange}}
{{/x-parent}}
{{/x-grandparent}}
Another solution that I would recommend be used more sparingly would be a service.
In our app, we often have cases like this where x-grandparent
, x-parent
and x-child
are what I like to call “local components”. They’re only ever used on one isolated section of the app (e.g. confined to one route namespace). In those cases, I think it can be more appropriate to use Ember.inject.service
. Instead of x-grandparent
or x-child
caching data or sending messages around, they could instead talk directly to a service. All of your application and domain state could live on that service making it more accessible to other components. In one of your common route’s setupController
hook you could initialize this service with any data it needs.
I will caution you again about getting carried away with this pattern. It definitely creates a more coupled set of components / service. However I think sometimes it’s just easier and more sane.