Read child component property from parent component


#1

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?


#2

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.