22
votes

I've got openModal action defined on application route. I'm trying to call this action from within a component.

If I use syntax for action bubbling:

{{my-component openModal="openModal"}}

then everything works as expected and I can trigger this action using this.sendAction("openModal").

However, I'm not sure how to get the same result using the new closure syntax:

{{my-component openModal=(action "openModal")}}

In this case, Ember complains that there's no action openModal defined on the controller. Do I have to define this action on every controller that uses my-component? Is there a way to somehow use target option to tell Ember that this action is defined on a route? Is it ok to mix bubbling and closure syntax in a single component?

I'm using Ember 2.0 beta 1.

5

5 Answers

23
votes

Until routable components are introduced somewhere in Ember 2.1 or 2.2 or 2.3 or 2.4 or 2.5 or 2.6 or 2.7, it is impossible to pass a closure action from a route.

For now, you can only pass closure actions from a controller and on to child components.

UPD: Miko Paderes hints that an addon is available: https://github.com/dockyard/ember-route-action-helper

12
votes

Try this:

{{my-component openModal=(action send "openModal")}}

...which makes use of the send method on the controller.

I can't say I fully understand it given that the second argument to send is the context but it is still passing additional arguments to my action in the route correctly. I'm currently using version 2.4.5.

8
votes

You can send closure actions to the route by proxying them through the controller. This works with any Ember version that supports closure actions. The only caveat is that the action name must differ from controller and route.

For instance, given this template:

{{foo-component myAction=(action 'foo')}}

You would need a foo action on your controller, which could proxy to the route:

proxyFooTo: 'fooBar',
actions: {

  foo(context) {
    this.send('proxyFooTo', context);
  }

}

Then on the route

actions: {
  fooBar(context) {  ... handle fooBar ... } 
}
3
votes

It is also possible to avoid creating a controller, or if there is one already avoid adding more logic in it, by specifying the target property

http://emberjs.jsbin.com/fiwokenoyu/1/edit?html,js,output

in route

js

setupController(controller,model){
  this._super(...arguments);
  controller.set('theRoute', this);
},
actions:{
  openModal(data){
    /*...*/
  }
}

in template

hbs

{{my-component openModalAction=(action 'openModal' target=theRoute)}}
0
votes

Try the Ember add-on called ember-route-action-helper,

https://github.com/dockyard/ember-route-action-helper

You can just replace route-action with action at the time "routable- components" become available.

{{todo-list todos=model addTodo=(route-action "addTodo")}}

So the code above has a similar effect as

Inside the route,

setupController(controller,model){
  this._super(...arguments);
  controller.set('theRoute', this);
},
actions:{
  addTodo(data){
    /*...*/
  }
}

Inside the HBS,

{{todo-list addTodo=(action 'addTodo' target=theRoute)}}