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');