« All deprecation guides

Deprecation Guide for Action helper and modifier

until: 6.0.0
id: template-action

Scenario: action is passed a string

Before:

<button type="button" {{action "plusOne"}}>
  Click Me
</button>

After:

<button type="button" {{on 'click' this.plusOne}}>
  Click Me
</button>

or, if plusOne is passed in as an argument:

<button type="button" {{on 'click' @plusOne}}>
  Click Me
</button>

If the plusOne action is in an actions object, it needs to move out:

For Glimmer components

Before:

import Component from '@glimmer/component';

export default class Demo extends Component {
    actions = {
        plusOne() {
           /* ... */ 
        }
    }
}

After:

import Component from '@glimmer/component';
import { action } from '@ember/object';

export default class Demo extends Component {
    @action
    plusOne() {
       /* ... */ 
    }
}

or

For Classic Components with native classes

Before:

import Component from '@ember/component';

export default class Demo extends Component {
    doMath() {
      this.send('plusOne');
    }

    actions = {
        plusOne() {
           /* ... */ 
        }
    }
}

After:

import Component from '@ember/component';
import { action } from '@ember/object';

export default class Demo extends Component {
    doMath() {
      this.plusOne();
    }

    @action
    plusOne() {
       /* ... */ 
    }
}

or

For Classic Components with EmberObject.extend

Before:

import Component from '@ember/component';

export default Component.extend({
    actions: {
        plusOne() {
           /* ... */ 
        }
    }
})

After:

import Component from '@ember/component';
import { action } from '@ember/object';

export default Component.extend({
  plusOne: action(function() {
      /* ... */ 
  }),
})

If (action) or {{action}} is passed a string, it's possible that the referenced method is declared on the caller, and not the immediate component -- that is, (action) and {{action}} bubble up the render tree from route templates -> controllers -> routes.

Note that @action is completely different from (action) or {{action}} (and is partly a motivator for deprecating (action) and {{action}}, to reduce ambiguity).

@action binds the this on the method to the instance of the class.

Scenario: action is passed a function reference

Before:

<SomeComponent @update={{action this.plusOne}} />

After

<SomeComponent @update={{this.plusOne}} />

Scenario: action is passed parameters

Before:

<SomeComponent @update={{action this.plus 1}} />

After:

<SomeComponent @update={{fn this.plus 1}} />

Scenario: action is used with mut

Before:

<SomeComponent @update={{action (mut @value.property}} />

After:

// parent.js
import Component from '@glimmer/component';
import { action } from '@ember/object';

export default class SomeComponent extends Component {
    @action
    handleUpdate(value) {
        this.args.property = value; 
    }
}
{{! parent.hbs }}
<SomeComponent @update={{this.handleUpdate}} />

Related, Combining function arguments with action functions

For more background, read the RFC