Skip to content

Commit f847f79

Browse files
authoredMar 28, 2024
Merge pull request #20653 from NullVoxPopuli/deprecate-action
Implement RFC#1006, Deprecate `(action)` and `{{action}}`
2 parents 0822efd + de07242 commit f847f79

28 files changed

+922
-390
lines changed
 

‎packages/@ember/-internals/deprecations/index.ts

+9
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,15 @@ export const DEPRECATIONS = {
9797
until: '6.0.0',
9898
url: 'https://deprecations.emberjs.com/v5.x/#toc_deprecate-implicit-route-model',
9999
}),
100+
DEPRECATE_TEMPLATE_ACTION: deprecation({
101+
id: 'template-action',
102+
url: 'https://deprecations.emberjs.com/id/template-action',
103+
until: '6.0.0',
104+
for: 'ember-source',
105+
since: {
106+
available: '5.9.0',
107+
},
108+
}),
100109
};
101110

102111
export function deprecateUntil(message: string, deprecation: DeprecationObject) {

‎packages/@ember/-internals/glimmer/lib/helpers/action.ts

+5
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import { get } from '@ember/-internals/metal';
55
import type { AnyFn } from '@ember/-internals/utility-types';
66
import { assert } from '@ember/debug';
7+
import { DEPRECATIONS, deprecateUntil } from '@ember/-internals/deprecations';
78
import { flaggedInstrument } from '@ember/instrumentation';
89
import { join } from '@ember/runloop';
910
import { DEBUG } from '@glimmer/env';
@@ -278,6 +279,10 @@ export const ACTIONS = new WeakSet();
278279
@public
279280
*/
280281
export default internalHelper((args: CapturedArguments): Reference<Function> => {
282+
deprecateUntil(
283+
`Usage of the \`(action)\` helper is deprecated. Migrate to native functions and function invocation.`,
284+
DEPRECATIONS.DEPRECATE_TEMPLATE_ACTION
285+
);
281286
let { named, positional } = args;
282287
// The first two argument slots are reserved.
283288
// pos[0] is the context (or `this`)

‎packages/@ember/-internals/glimmer/lib/modifiers/action.ts

+5
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { InternalOwner } from '@ember/-internals/owner';
2+
import { DEPRECATIONS, deprecateUntil } from '@ember/-internals/deprecations';
23
import { uuid } from '@ember/-internals/utils';
34
import { ActionManager, EventDispatcher, isSimpleClick } from '@ember/-internals/views';
45
import { assert } from '@ember/debug';
@@ -204,6 +205,10 @@ class ActionModifierManager implements InternalModifierManager<ActionState, obje
204205
}
205206

206207
install(actionState: ActionState): void {
208+
deprecateUntil(
209+
`Usage of the \`{{action}}\` modifier is deprecated. Migrate to native functions and function invocation.`,
210+
DEPRECATIONS.DEPRECATE_TEMPLATE_ACTION
211+
);
207212
let { element, actionId, positional } = actionState;
208213

209214
let actionName;

‎packages/@ember/-internals/glimmer/tests/integration/application/actions-test.js

+21-7
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,16 @@
1-
import { moduleFor, ApplicationTestCase, RenderingTestCase, runTask } from 'internal-test-helpers';
1+
import {
2+
testUnless,
3+
moduleFor,
4+
ApplicationTestCase,
5+
RenderingTestCase,
6+
runTask,
7+
} from 'internal-test-helpers';
28

39
import Controller from '@ember/controller';
410
import { getDebugFunction, setDebugFunction } from '@ember/debug';
511

612
import { Component } from '../../utils/helpers';
13+
import { DEPRECATIONS } from '../../../../deprecations';
714

815
const originalDebug = getDebugFunction('debug');
916
const noop = function () {};
@@ -14,16 +21,21 @@ moduleFor(
1421
constructor() {
1522
setDebugFunction('debug', noop);
1623
super(...arguments);
24+
25+
expectDeprecation(
26+
/Usage of the `\{\{action\}\}` modifier is deprecated./,
27+
DEPRECATIONS.DEPRECATE_TEMPLATE_ACTION.isEnabled
28+
);
1729
}
1830

1931
teardown() {
2032
setDebugFunction('debug', originalDebug);
2133
}
2234

23-
['@test actions in top level template application template target application controller'](
24-
assert
25-
) {
26-
assert.expect(1);
35+
[`${testUnless(
36+
DEPRECATIONS.DEPRECATE_TEMPLATE_ACTION.isRemoved
37+
)} actions in top level template application template target application controller`](assert) {
38+
assert.expect(2);
2739

2840
this.add(
2941
'controller:application',
@@ -46,8 +58,10 @@ moduleFor(
4658
});
4759
}
4860

49-
['@test actions in nested outlet template target their controller'](assert) {
50-
assert.expect(1);
61+
[`${testUnless(
62+
DEPRECATIONS.DEPRECATE_TEMPLATE_ACTION.isRemoved
63+
)} actions in nested outlet template target their controller`](assert) {
64+
assert.expect(2);
5165

5266
this.add(
5367
'controller:application',

‎packages/@ember/-internals/glimmer/tests/integration/components/contextual-components-test.js

+11-15
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { DEBUG } from '@glimmer/env';
22
import { moduleFor, RenderingTestCase, applyMixins, strip, runTask } from 'internal-test-helpers';
33

44
import { isEmpty } from '@ember/utils';
5+
import { action } from '@ember/object';
56
import { A as emberA } from '@ember/array';
67

78
import { Component } from '../../utils/helpers';
@@ -773,18 +774,16 @@ moduleFor(
773774

774775
this.registerComponent('my-action-component', {
775776
ComponentClass: Component.extend({
776-
actions: {
777-
changeValue() {
778-
this.incrementProperty('myProp');
779-
},
780-
},
777+
changeValue: action(function () {
778+
this.incrementProperty('myProp');
779+
}),
781780
}),
782781
template: strip`
783782
{{#my-component my-attr=this.myProp as |api|}}
784783
{{api.my-nested-component}}
785784
{{/my-component}}
786785
<br>
787-
<button onclick={{action 'changeValue'}}>Change value</button>`,
786+
<button onclick={{this.changeValue}}>Change value</button>`,
788787
});
789788

790789
this.render('{{my-action-component myProp=this.model.myProp}}', {
@@ -839,13 +838,12 @@ moduleFor(
839838
['@test parameters in a contextual component are mutable when value is a param'](assert) {
840839
// This checks that a `(mut)` is added to parameters and attributes to
841840
// contextual components when it is a param.
842-
843841
this.registerComponent('change-button', {
844842
ComponentClass: Component.extend().reopenClass({
845843
positionalParams: ['val'],
846844
}),
847845
template: strip`
848-
<button {{action (action (mut this.val) 10)}} class="my-button">
846+
<button {{on "click" (fn (mut this.val) 10)}} class="my-button">
849847
Change to 10
850848
</button>`,
851849
});
@@ -892,15 +890,13 @@ moduleFor(
892890
this.registerComponent('outer-component', {
893891
ComponentClass: Component.extend({
894892
message: 'hello',
895-
actions: {
896-
change() {
897-
this.set('message', 'goodbye');
898-
},
899-
},
893+
change: action(function () {
894+
this.set('message', 'goodbye');
895+
}),
900896
}),
901897
template: strip`
902898
message: {{this.message}}{{inner-component message=this.message}}
903-
<button onclick={{action "change"}} />`,
899+
<button onclick={{this.change}} />`,
904900
});
905901

906902
this.registerComponent('inner-component', {
@@ -1447,7 +1443,7 @@ class MutableParamTestGenerator {
14471443
positionalParams: ['val'],
14481444
}),
14491445
template: strip`
1450-
<button {{action (action (mut this.val) 10)}} class="my-button">
1446+
<button {{on "click" (fn (mut this.val) 10)}} class="my-button">
14511447
Change to 10
14521448
</button>`,
14531449
});

‎packages/@ember/-internals/glimmer/tests/integration/components/curly-components-test.js

+32-17
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@ import {
77
equalsElement,
88
runTask,
99
runLoopSettled,
10+
testUnless,
1011
} from 'internal-test-helpers';
1112

13+
import { action } from '@ember/object';
1214
import { run } from '@ember/runloop';
1315
import { DEBUG } from '@glimmer/env';
1416
import { tracked } from '@ember/-internals/metal';
@@ -20,6 +22,7 @@ import { A as emberA } from '@ember/array';
2022

2123
import { Component, compile, htmlSafe } from '../../utils/helpers';
2224
import { backtrackingMessageFor } from '../../utils/debug-stack';
25+
import { DEPRECATIONS } from '../../../../deprecations';
2326

2427
moduleFor(
2528
'Components test: curly components',
@@ -1441,25 +1444,23 @@ moduleFor(
14411444
componentInstance = this;
14421445
},
14431446

1444-
actions: {
1445-
click() {
1446-
let currentCounter = this.get('counter');
1447+
myClick: action(function () {
1448+
let currentCounter = this.get('counter');
14471449

1448-
assert.equal(currentCounter, 0, 'the current `counter` value is correct');
1450+
assert.equal(currentCounter, 0, 'the current `counter` value is correct');
14491451

1450-
let newCounter = currentCounter + 1;
1451-
this.set('counter', newCounter);
1452+
let newCounter = currentCounter + 1;
1453+
this.set('counter', newCounter);
14521454

1453-
assert.equal(
1454-
this.get('counter'),
1455-
newCounter,
1456-
"getting the newly set `counter` property works; it's equal to the value we just set and not `undefined`"
1457-
);
1458-
},
1459-
},
1455+
assert.equal(
1456+
this.get('counter'),
1457+
newCounter,
1458+
"getting the newly set `counter` property works; it's equal to the value we just set and not `undefined`"
1459+
);
1460+
}),
14601461
}),
14611462
template: `
1462-
<button {{action "click"}}>foobar</button>
1463+
<button {{on "click" this.myClick}}>foobar</button>
14631464
`,
14641465
});
14651466

@@ -3146,9 +3147,16 @@ moduleFor(
31463147
runTask(() => set(this.context, 'foo', 5));
31473148
}
31483149

3149-
['@test returning `true` from an action does not bubble if `target` is not specified (GH#14275)'](
3150+
[`${testUnless(
3151+
DEPRECATIONS.DEPRECATE_TEMPLATE_ACTION.isRemoved
3152+
)} returning \`true\` from an action does not bubble if \`target\` is not specified (GH#14275)`](
31503153
assert
31513154
) {
3155+
expectDeprecation(
3156+
/Usage of the `\{\{action\}\}` modifier is deprecated./,
3157+
DEPRECATIONS.DEPRECATE_TEMPLATE_ACTION.isEnabled
3158+
);
3159+
31523160
this.registerComponent('display-toggle', {
31533161
ComponentClass: Component.extend({
31543162
actions: {
@@ -3173,8 +3181,15 @@ moduleFor(
31733181
runTask(() => this.$('button').click());
31743182
}
31753183

3176-
['@test returning `true` from an action bubbles to the `target` if specified'](assert) {
3177-
assert.expect(4);
3184+
[`${testUnless(
3185+
DEPRECATIONS.DEPRECATE_TEMPLATE_ACTION.isRemoved
3186+
)} returning \`true\` from an action bubbles to the \`target\` if specified`](assert) {
3187+
assert.expect(5);
3188+
3189+
expectDeprecation(
3190+
/Usage of the `\{\{action\}\}` modifier is deprecated./,
3191+
DEPRECATIONS.DEPRECATE_TEMPLATE_ACTION.isEnabled
3192+
);
31783193

31793194
this.registerComponent('display-toggle', {
31803195
ComponentClass: Component.extend({

‎packages/@ember/-internals/glimmer/tests/integration/components/dynamic-components-test.js

+10-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import { DEBUG } from '@glimmer/env';
2-
import { moduleFor, RenderingTestCase, strip, runTask } from 'internal-test-helpers';
2+
import { moduleFor, RenderingTestCase, strip, runTask, testUnless } from 'internal-test-helpers';
33

44
import { set, computed } from '@ember/object';
55

66
import { Component } from '../../utils/helpers';
77
import { backtrackingMessageFor } from '../../utils/debug-stack';
8+
import { DEPRECATIONS } from '../../../../deprecations';
89

910
moduleFor(
1011
'Components test: dynamic components',
@@ -429,7 +430,9 @@ moduleFor(
429430
this.assertText('foo-bar Caracas Caracas arepas!');
430431
}
431432

432-
['@test component helper with actions'](assert) {
433+
[`${testUnless(
434+
DEPRECATIONS.DEPRECATE_TEMPLATE_ACTION.isRemoved
435+
)} component helper with actions`](assert) {
433436
this.registerComponent('inner-component', {
434437
template: 'inner-component {{yield}}',
435438
ComponentClass: Component.extend({
@@ -449,6 +452,11 @@ moduleFor(
449452
}),
450453
});
451454

455+
expectDeprecation(
456+
/Usage of the `\(action\)` helper is deprecated./,
457+
DEPRECATIONS.DEPRECATE_TEMPLATE_ACTION.isEnabled
458+
);
459+
452460
let actionTriggered = 0;
453461
this.registerComponent('outer-component', {
454462
template:

‎packages/@ember/-internals/glimmer/tests/integration/components/input-angle-test.js

+17-23
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { moduleFor, RenderingTestCase, runDestroy, runTask } from 'internal-test-helpers';
2-
import { set } from '@ember/object';
2+
import { action, set } from '@ember/object';
33

44
class InputRenderingTest extends RenderingTestCase {
55
$input() {
@@ -280,18 +280,16 @@ moduleFor(
280280
// this.assertSelectionRange(8, 8); //NOTE: this fails in IE, the range is 0 -> 0 (TEST_SUITE=sauce)
281281
}
282282

283-
['@test sends an action with `<Input @enter={{action "foo"}} />` when <enter> is pressed'](
283+
[`@test sends an action with \`<Input @enter={{this.foo}} />\` when <enter> is pressed`](
284284
assert
285285
) {
286286
assert.expect(2);
287287

288-
this.render(`<Input @enter={{action 'foo'}} />`, {
289-
actions: {
290-
foo(value, event) {
291-
assert.ok(true, 'action was triggered');
292-
assert.ok(event instanceof Event, 'Native event was passed');
293-
},
294-
},
288+
this.render(`<Input @enter={{this.foo}} />`, {
289+
foo: action(function (value, event) {
290+
assert.ok(true, 'action was triggered');
291+
assert.ok(event instanceof Event, 'Native event was passed');
292+
}),
295293
});
296294

297295
this.triggerEvent('keyup', {
@@ -302,12 +300,10 @@ moduleFor(
302300
['@test sends `insert-newline` when <enter> is pressed'](assert) {
303301
assert.expect(2);
304302

305-
this.render(`<Input @insert-newline={{action 'foo'}} />`, {
306-
actions: {
307-
foo(value, event) {
308-
assert.ok(true, 'action was triggered');
309-
assert.ok(event instanceof Event, 'Native event was passed');
310-
},
303+
this.render(`<Input @insert-newline={{this.foo}} />`, {
304+
foo(value, event) {
305+
assert.ok(true, 'action was triggered');
306+
assert.ok(event instanceof Event, 'Native event was passed');
311307
},
312308
});
313309

@@ -316,18 +312,16 @@ moduleFor(
316312
});
317313
}
318314

319-
['@test sends an action with `<Input @escape-press={{action "foo"}} />` when <escape> is pressed'](
315+
['@test sends an action with `<Input @escape-press={{this.foo}} />` when <escape> is pressed'](
320316
assert
321317
) {
322318
assert.expect(2);
323319

324-
this.render(`<Input @escape-press={{action 'foo'}} />`, {
325-
actions: {
326-
foo(value, event) {
327-
assert.ok(true, 'action was triggered');
328-
assert.ok(event instanceof Event, 'Native event was passed');
329-
},
330-
},
320+
this.render(`<Input @escape-press={{this.foo}} />`, {
321+
foo: action(function (value, event) {
322+
assert.ok(true, 'action was triggered');
323+
assert.ok(event instanceof Event, 'Native event was passed');
324+
}),
331325
});
332326

333327
this.triggerEvent('keyup', { key: 'Escape' });

0 commit comments

Comments
 (0)