Passing down actions to child components

Hi,

In the previous versions of ember.js you could pass actions down from a current component like

{{mycomponent  myaction=(action "clickCategory) }}

Then you could go into mycomponent you could use another component in it:

{{mychildcomponent myaction2=(action myaction)}} /* without quotes */

and then in mychildcomponent template you can use it straight away without the need to put it in the js of mychildcomponent.

In the new Glimmer/Octane stuff, how is this done? The Ember Guides seem not to cover it so I am duplicating the actions in child components and calling up to this.args.myaction2().

Basically put can I pass an action down a few layers of components without having to define a @action in the js of each descendant?

Yes!

// app/components/top-level.js
import Component from '@glimmer/component';
import { action } from "@ember/object";

export default class TopLevelComponent extends Component {
  @action
  handleClick() {
    // your implementation here
  }
}
{{!  app/components/top-level.hbs }}

Passing the action from `this.handleClick`:

<MiddleLevel @handleClick={{this.handleClick}} />
{{! app/components/middle-level.hbs }}

Passing forward the named argument that we were passed (@handleClick):

<LowerLevel @handleClick={{@handleClick}} />
{{! app/components/lower-level.hbs }}

Using the named argument (@handleClick) passed in from app/components/middle-level.hbs:

<button {{on 'click' @handleClick}}>Click Me!</button>
1 Like

Hi rwjblue,

Thanks a lot for your reply. Do you know if it is possible for the top level component (as above, “top-level”) to be a classic ember component, but the descendants be Octane (just as you have them)?

Curveball! But I always seem to run into these things straight away

The awkwardness comes with using the glimmer tag name in the top-level classic component html:

<CategoryComponent
  	@model={{model}} 
	@filterid={{this.filterid}}
	@onInsertFurniture={{this.onInsertFurniture}}>
</CategoryComponent>

How do I express onInsertFurniture (an action from a classic ember component “top-level”) in this new glimmer way of bringing in a component? Doing it the way stated there {{this.onInsertFurniture}} seems to be just undefined? It is not ‘seeing’ it under the actions:{} section of the js file. If you take onInsertFurniture out of the actions part of the js file, and instead, declare it as a normal function - it does get passed down, but then when you call it from a child component you lose the ability to call “this.get” inside it.

Thanks for pointing out the nested components methods, penny is dropping.

I’ve worked out the answer to my second problem through trial and error, you just tell the tag it’s an action. I didn’t try it because I was thinking that would only work on an old class component.

 <CategoryComponent
  	    @model={{model}} 
		@filterid={{this.filterid}}
		@onInsertFurniture={{action "onInsertFurniture"}}>
	</CategoryComponent>

Yeah the “action” modifier does the expected binding to “this”, glad you got it figured out!

1 Like

You can also use @action on the methods of both old and new-style components, and then use {{this.onInsertFurniture}}.

Of you mean, just marking any actions as @action in the JS file would work ?

Also, a related question would be on passing properties ? Is there any difference in accessing them as well ?

Of you mean, just marking any actions as @action in the JS file would work ?

Yes, provided your component is a JS class and your function is a property of that class:

  • If you’re using Component.extends({ actions: { classicAction(){} } }), the @action decorator isn’t going to work on classicAction.
  • If you’re using class MyComp extends Component { modernAction(){} }, @action on modernAction is all you need.

What’s really going on:

When you pass a function in the handlebar, it needs its this to be bound to your instance. @action does that to the member function when the class is constructed, and that’s all it does. A standard object bind operation on init will do the same thing, but the syntax is messier.

The action modifier and helper in the handlebars do that, too – among several other things, and that’s their problem. “Magic” mechanisms that do several things at once seem to offer a promise of not having to think about the nuts and bolts. Then, when you have to think about how things work after all, the “magic” makes the actual behavior of code harder to reason about. This is why we recommend moving away from {{action}} at your leisure :slight_smile: , but they still do what they always did.

2 Likes