In the past we have looked at actions in Ember. We have seen how to call and action from the template and we have looked at sending actions from a component up the chain by way of closure actions. However, in our overview we glossed over a few small options for the sake of being concise and for clarity. Today we will attempt to remedy that by looking at actions with a little less duplication.

The exercises that we look at here can easily be duplicated with Ember Twiddle.

To start let's look at a basic example of calling an action from a template on a component (or a controller as in this case they are functionally identical).

In our component's javascript file, we will add two functions; one in our actions object and one outside of the actions object:

function1(){
  console.log('function1 called not contained in the actions object.');
},
actions: {
  action1(){
    console.log('action1 called inside the actions object.');
  }
}

Now, inside the corresponding template include two buttons that trigger these functions.

<button {{action 'action1'}}>Action</button>
<button {{action function1}}>Function</button>

You will notice that the action is called as a quoted string. While the function that is not in the actions object is called as the property key. This also has implications for closure actions that are passed to components. To this point, if we wanted to trigger a closure action from inside a component we added an action to the component's javascript and just called the closure action from that internal action. It looked like this:

Parent Component's template:

{{child-component someAction=(action 'action1')}}

The child component's template would look like this:

<button {{action 'someInternalAction'}}>Fire Action</button>

The child component's JavaScript looks like this:

actions: {
  someInternalAction(){
    this.get('someAction')();
  }
}

This would work to fire the action on the parent component and this is a great way to go if you need to use the return value from the parent component's action in some way on the child. But if you are just firing the action, there is no need to create the duplication in child component's JavaScript. You can just do this:

Parent Component's template:

{{child-component someAction=(action 'action1')}}

The child component's template would look like this:

<button {{action someAction}}>Fire Action</button>

The child component's JavaScript looks like this:

  // Nothing to see here. :)

Notice, we are directly calling the action by way of the property that was passed in to the component and all we had to just use the property name instead of passing in a string.

One final thing that is probably apparent by now: you can can pass functions from the parent component to the child in the same way as we reference properties. If we wanted to pass our function1 function from the parent component to the child component, everything would work exactly the same except again, we use the property name rather than a string.

The parent component's template:

{{child-component someAction=(action function1)}}

The child component's template would look like this:

<button {{action someAction}}>Fire Action</button>

The child component's JavaScript looks like this:

  // Nothing to see here. :)

One other thing worth noting about closure actions is 'bubbling'. If you encounter any items in the Ember docs about actions bubbling, closure actions wont work quite how you expect. Since, you are no sending an action but calling a function, your action wont bubble up the route chain. If you want to call a route action from a component using a closure action, you will need to either have an intermediary action in the controller that is called by the component then sends an action to the route. Or you can use an Ember addon from the folks at Dockyard called Ember Route Action Helper.

That's it for today. See you tomorrow.