Part 1: Ember send() and sendAction()

6 min read • 4th June 2018
Code snippet explaining send actions between component, controller and route

Note
sendAction is deprecated in favor of closure actions. Here's the RFC: https://github.com/emberjs/rfcs/pull/335. You can also read about Closure actions here


Some of us might find the use of send and sendAction in Ember components a bit confusing. I would try to explain how send and sendActions communicate with controllers and routes, with some examples of common pitfalls you might encounter.

Let's assume we have the following cases to handle:

  1. Passing data from a component to its controller/route.
  2. Passing data from a controller to its route.
  3. Passing data from a nested component to its parent component.

Ember follows a convention called "Data Down Actions Up" or famously the DDAU approach. What does that mean? Well, in simple words, we have to send data down and trigger actions up.

From the basics of working with Ember, we know it's router.js which is the entry point to identify which route/controller/template is to be invoked.

Let's assume, we have a route called foo.show. Our router file is routes/foo/show.js and if needed a controller in the same naming convention controllers/foo/show.js. When a route is created (via ember g), Ember CLI also creates a template in the path templates/foo/show.hbs. Let's say we have a component called that will be rendered inside our show.hbs template. This is how our project structure would look like

Project folder structure
On the left is our project folder and on the right is the Router.js configuration.

On the left is our project folder and on the right is the Router.js configuration.


Link to this section1. Passing data from a component to its controller/route

When you want to communicate from a component to its controller/route, you simply call sendAction(). Remember! sendAction() can be used from a component but not from controllers/routes.

// routes/foo/bar.js
export default Route.extend();

// controllers/foo/bar.js
export default Controller.extend({
  actions: {
    sendData(data) {
      console.log(data);
    }
  }
});

// components/foo-bar.js
export default Component.extend({
  actions: {
    sendDataToController() {
      this.sendAction('sendData', 'foo');
    }
  }
});

Note
If you want to trigger an action in a route from a component, you can just do the same code as above, in which case the controller needn't have the action.

// routes/foo/bar.js
export default Route.extend({
  actions: {
    sendData(data) {
      console.log(data);
    }
  }
});

// controllers/foo/bar.js
export default Controller.extend();

// components/foo-bar.js
export default Component.extend({
  actions: {
    sendDataToController() {
      this.sendAction('sendData', 'foo');
    }
  }
});

Check out the Github GIST and Ember twiddle here.


Link to this section2. Passing data from a controller to its route

When you want to communicate from a controller to its route, you simply call send() . Here sendAction() will not work!

// routes/foo/bar.js
export default Route.extend({
  actions: {
    sendData(data) {
      console.log(`Data received in route as: ${data}`);
    }
  }
});

// controllers/foo/bar.js
export default Controller.extend({
  actions: {
    sendData(data) {
      console.log(`Data is sent to controller as: ${data}. Let's propagate it to the route`);

      // Using same action name as controller will lead to "Max call stack" issue.
      this.send('sendDataToRoute', data);
    }
  }
});

// components/foo-bar.js
export default Component.extend({
  actions: {
    sendDataToController() {
      this.sendAction('sendData', 'foo');
    }
  }
});

But the above doesn't make sense as the actions not present in Controller would internally propagate to its route.

// routes/foo/bar.js
export default Route.extend({
  actions: {
    sendData(data) {
      console.log(data);
    }
  }
});

// controllers/foo/bar.js
export default Controller.extend();

// components/foo-bar.js
export default Component.extend({
  actions: {
    sendDataToController() {
      this.sendAction('sendData', 'foo');
    }
  }
});

Note
When you communicate from a controller to its route, the mantra is to simply call this.send().

Check out the Github GIST and Ember twiddle here.


Link to this section3. Passing data from a nested component to its parent component

When you want to communicate from a component to its parent component, then the call to be used is sendAction().

<!-- templates/foo-bar-2.hbs -->
{{foo-bar-2 sendDataToParent="sendDataToParent"}}
// components/foo-bar.js
export default Component.extend({
  actions: {
    sendDataToParent() {
      this.sendAction('sendData', 'foo');
    }
  }
});

// components/foo-bar-child.js
export default Component.extend({
  actions: {
    sendDataToParent() {
      this.sendAction('sendDataToParent', 'foo');
    }
  }
});

What if you want to trigger an action in its route?

All you need to do is simply follow Step 2. But there is an Ember add-on to do this for you, which will propagate your action from foo-bar2 to routes/foo-bar/show.js route called ember-route-actions-helper.

Note
If you wish to trigger an action in foo-bar2 itself, then you call this.send() and not this.sendAction()

Check out the Github GIST and Ember twiddle here.