From 60f73af2ac3f788a14ece24395a0cc51a6ae3879 Mon Sep 17 00:00:00 2001 From: NullVoxPopuli <199018+NullVoxPopuli@users.noreply.github.com> Date: Mon, 27 Jul 2026 11:30:40 -0400 Subject: [PATCH] Deprecation guide for https://github.com/emberjs/rfcs/pull/1041 --- app/templates/index.hbs | 3 + content/ember/v7/target-action-support.md | 199 +++++++++++++++++++++ lib/content-docs-generator/index.js | 1 + tests/acceptance/visual-regression-test.js | 10 ++ 4 files changed, 213 insertions(+) create mode 100644 content/ember/v7/target-action-support.md diff --git a/app/templates/index.hbs b/app/templates/index.hbs index 6f15d81d..86e1d438 100644 --- a/app/templates/index.hbs +++ b/app/templates/index.hbs @@ -55,6 +55,9 @@
  • v6.x
  • +
  • + v7.x +
  • diff --git a/content/ember/v7/target-action-support.md b/content/ember/v7/target-action-support.md new file mode 100644 index 00000000..90455ad3 --- /dev/null +++ b/content/ember/v7/target-action-support.md @@ -0,0 +1,199 @@ +--- +title: Target Action Support +until: 8.0.0 +since: 7.3.0 +--- + +The `send` method (from the `ActionHandler` and `ActionSupport` mixins) and the `triggerAction` method (from the `TargetActionSupport` mixin) are deprecated. + +These APIs are legacy patterns that predate native classes and Glimmer components. Their primary use case was to support the `{{action}}` helper and modifier, which [has already been deprecated](/deprecations/v5.x/#toc_template-action). The modern approach is to use standard class methods (optionally decorated with `@action` to bind `this`) and to pass functions directly, following the Data Down, Actions Up (DDAU) pattern. + +### Scenario: `send` is used to call an action on the same object + +Instead of dispatching a method by name with `this.send('someAction')`, define a regular method and call it directly. + +#### For Classic Components with native classes + +Before: +```javascript +import Component from '@ember/component'; +import { action } from '@ember/object'; + +export default class Demo extends Component { + doMath() { + this.send('plusOne'); + } + + @action + plusOne() { + /* ... */ + } +} +``` + +After: +```javascript +import Component from '@ember/component'; +import { action } from '@ember/object'; + +export default class Demo extends Component { + doMath() { + this.plusOne(); + } + + @action + plusOne() { + /* ... */ + } +} +``` + +#### For Classic Components with EmberObject.extend + +Before: +```javascript +import Component from '@ember/component'; + +export default Component.extend({ + doMath() { + this.send('plusOne'); + }, + + actions: { + plusOne() { + /* ... */ + }, + }, +}); +``` + +After: +```javascript +import Component from '@ember/component'; +import { action } from '@ember/object'; + +export default Component.extend({ + doMath() { + this.plusOne(); + }, + + plusOne: action(function () { + /* ... */ + }), +}); +``` + +### Scenario: `send` is used to bubble an action from a controller to a route + +Previously, calling `send` on a controller would look for the action on the controller first, and then bubble up through the active routes until a matching action was found. + +Instead of relying on bubbling, move the shared logic to where it is needed — for example into a service that both the controller and route can inject. + +Before: +```javascript +// app/controllers/document.js +import Controller from '@ember/controller'; +import { action } from '@ember/object'; + +export default class DocumentController extends Controller { + @action + save() { + // bubbles up to the route's `save` action + this.send('saveDocument'); + } +} +``` + +```javascript +// app/routes/document.js +import Route from '@ember/routing/route'; +import { action } from '@ember/object'; + +export default class DocumentRoute extends Route { + @action + saveDocument() { + /* ... saving logic */ + } +} +``` + +After: +```javascript +// app/services/documents.js +import Service from '@ember/service'; + +export default class DocumentsService extends Service { + saveDocument() { + /* ... saving logic */ + } +} +``` + +```javascript +// app/controllers/document.js +import Controller from '@ember/controller'; +import { action } from '@ember/object'; +import { service } from '@ember/service'; + +export default class DocumentController extends Controller { + @service documents; + + @action + save() { + this.documents.saveDocument(); + } +} +``` + +### Scenario: `triggerAction` is used to invoke an action on a target + +`triggerAction` looked up a named action on the object (or on its `target`) and invoked it. Instead, call the method directly, or invoke a function that was passed in as an argument. + +Before: +```javascript +import Component from '@ember/component'; + +export default Component.extend({ + actions: { + save() { + /* ... saving logic */ + }, + }, + + doSave() { + this.triggerAction({ action: 'save' }); + }, +}); +``` + +After: +```javascript +import Component from '@ember/component'; +import { action } from '@ember/object'; + +export default Component.extend({ + save: action(function () { + /* ... saving logic */ + }), + + doSave() { + this.save(); + }, +}); +``` + +If the action lived on a `target`, invoke the function on that object directly, or — better — have the caller pass the function in as an argument (Data Down, Actions Up): + +Before: +```javascript +this.triggerAction({ action: 'save', target: this.someTarget }); +``` + +After: +```javascript +this.someTarget.save(); +// or, if the function is passed in as an argument +this.args.save(); +``` + +For more background, read the [RFC](https://github.com/emberjs/rfcs/pull/1041). diff --git a/lib/content-docs-generator/index.js b/lib/content-docs-generator/index.js index bb5641d1..1d769502 100644 --- a/lib/content-docs-generator/index.js +++ b/lib/content-docs-generator/index.js @@ -10,6 +10,7 @@ const contentFolders = [ 'ember/v4', 'ember/v5', 'ember/v6', + 'ember/v7', 'ember-data/v2', 'ember-data/v3', 'ember-data/v4', diff --git a/tests/acceptance/visual-regression-test.js b/tests/acceptance/visual-regression-test.js index 7c1eb1b9..24491e5a 100644 --- a/tests/acceptance/visual-regression-test.js +++ b/tests/acceptance/visual-regression-test.js @@ -130,6 +130,16 @@ module('Acceptance | visual regression', function (hooks) { await percySnapshot('ember-6.x'); + // v7.x Ember + await click('[data-test-main-deprecations-link]'); + await click('[data-test-ember-7-link] > a'); + + assert + .dom('[data-test-deprecations-added-in]') + .hasText('Deprecations Added in Ember 7.x'); + + await percySnapshot('ember-7.x'); + // Individual page await visit('/id/ember-polyfills-deprecate-assign'); assert.dom('h1').hasText('Deprecation Guide for Ember.assign');