Skip to content

Instantly share code, notes, and snippets.

@lmcardle
Last active August 18, 2022 18:01
Show Gist options
  • Save lmcardle/781446d11174557e4a6d to your computer and use it in GitHub Desktop.
Save lmcardle/781446d11174557e4a6d to your computer and use it in GitHub Desktop.

Ember Closure Actions

When building complex components, the traditional way of passing and calling actions was cumbersome. Often while building and constructing components, it is preferable to build them in a composable manner and keep each composable part completely isolated and only concerned about itself. However the traditional approach to calling actions made this difficult, rather the parent component always needed to know about the child components actions. For example, suppose I have three components, all related to building a table (my-table, my-table-header-row, my-table-header-cell). Anytime I click on the "my-table-header-cell", the "cellClicked" action should be called and passed the key for that cell. The traditional approach would look something like this:

{{! /templates/application.hbs}}
{{#my-table}}
  {{#my-table-header-row}}
    {{#my-table-header-cell key="cell1" clickAction="cellClicked"}}Cell 1{{/my-table-header-cell}}
    {{#my-table-header-cell key="cell2" clickAction="cellClicked"}}Cell 2{{/my-table-header-cell}}
  {{/my-table-header-row}}
{{/mytable}}
// /controllers/application.js
actions: {
  cellClicked(key) {
    this.set('sortKey', key);
  }
}
// /components/my-table.js
actions: {
  clickAction(key) {
    this.sendAction('clickAction', key);
  }
}
// /components/my-table-header-row.js
actions: {
  clickAction(key) {
    this.sendAction('clickAction', key);
  }
}
{{! /templates/components/my-table-header-cell.hbs}}
<span {{action "clickAction"}}>{{yield}}</span>
// /components/my-table-header-cell.js
actions: {
  clickAction() {
    this.sendAction('clickAction', this.get('key'));
  }
}

In the above example, the parent component has to know about the actions for all children and grandchildren, etc. Instead, it would be preferable for the parent to only be concerned about itself and for the children and grandchildren to not need to go through their parents in order to call a passed action. That is where "closure actions" come in and they are available as of Ember 1.13. Using the above example, our implementation would change to:

{{! /templates/application.hbs}}
{{#my-table}}
  {{#my-table-header-row}}
    {{#my-table-header-cell clickAction=(action 'cellClicked' 'cell1')}}
      Cell 1
    {{/my-table-header-cell}}
    {{#my-table-header-cell clickAction=(action 'cellClicked' 'cell2')}}
      Cell 2
    {{/my-table-header-cell}}
  {{/my-table-header-row}}
{{/mytable}}
// /controllers/application.js
actions: {
  cellClicked(key) {
    this.set('sortKey', key);
  }
}
{{! /templates/components/my-table-header-cell.hbs}}
<span {{action "clickAction"}}>{{yield}}</span>
// /components/my-table-header-cell.js
actions: {
  clickAction() {
    this.get('clickAction')();
  }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment