Triggering a component action on route change - a relative newbie's confusion


#1

Hi all,

I’m a few weeks into playing with Ember, coming from a server-side MVC background. I’ve done my best to use newer Ember 2 conventions, to break up my UI into reusable isolated components, embrace “data down, actions up”, etc. But I’m hitting an issue where I simply don’t understand how to do what I want, and Google hasn’t been able to help me. Since I suspect this is more of a fundamental conceptual misunderstanding on my part more than just a “how do I”, I hope you don’t mind my posting it here instead of SO.

I’ve got what seems to be a pretty standard master-detail “drill-down” chain of routes. What I’d like is that when I transition one level deeper into a route, the UI elements in the master template compress, to take up less space on the screen while still remaining present for context. The components themselves are all capable of doing so via an action – triggering this manually with a click works fine. However, I can’t figure out how to have the transition from one route to another send the compress action to the components which live “above” both the origin and destination routes. It’s not a matter of data or models changing… just a UI effect.

I’ve googled around a bit, but the only relatively similar examples I can find involve views and controllers, which I understand ideally I’m not supposed to be using in this new 2.5 project. I feel like this can’t be such a crazy or unusual situation. As I mentioned, I suspect I’m just fundamentally misunderstanding how such things are supposed to be done in Ember. Any thoughts? Thanks!


#2

Pass an attribute (data down) indicating whether your component should display in expanded or non-expanded state. In your parent route’s template, pass expanded=true and in your child route’s template pass expanded=false.


#3

Thanks for responding! What would be the mechanics of that? I can and do pass data from a route’s template into a component in that template, but the deeper detail route and its template seemingly has no access to the components back in the higher-up route.


#4

Ah, I see the issue, your component is being defined in the master route but needs a property value different if the app is in the detail route.

For the moment, until routeable components are in Ember, this still requires manipulation of the controller in the routes.

Here is a twiddle with the basic solution: https://ember-twiddle.com/f5df7801bcb57e48131d6af92bc2d805?openFiles=routes.master.detail.js%2C&route=%2Fmaster%2Fdetail


#5

Ah thanks – I think I was doing my best to avoid components, as I knew they were deprecated. Do you have any good pointers on where I can learn more about using them (hopefully in the most forward-compatible way possible)? The Guide only has a single page that isn’t much help.


#6

Thanks again for the Twiddle – it was extremely clear, and I got a similar effect working in my code.

One unexpected side-effect, though, is that it appears to have created a two-way binding between the route/controller-level attribute and the component-level attribute flags. That is, if the route’s isExpanded attribute (to use your example) is toggled, all the components fall in line – as I expected. However, if the I or the user manually expands or compresses an item, it sets the containing route’s isExpanded as well – affecting all other components.

So far, everything I’ve done with components has had one-way binding. Is this a side effect of using controllers? I’d still like to be able to control the components individually and leave the route-level flag unchained. Is that possible?


#7

@jb_jb Two way bindings are currently the default, regardless of whether the context of a component is another component or a controller. This will change in the future, at the same time as angle-bracket component syntax is introduced, to avoid breaking compatibility.

To enforce one way binding today, consider using the readonly helper:

{{my-component myParam=(readonly myParam)}}

In the future of angle bracket components, you will need to use mut to get a two way binding:

<my-component myParam={{mut myParam}}>

#8

Thank you again – that’s really good to know.

So I don’t keep bugging you and this board, is there a learning resource that you recommend? I’ve been a bit frustrated by the official guide’s omissions, but most of the tutorials I’m finding online seem very out-of-date.