Deprecate .sendAction
Summary
In old versions of Ember (< 1.13) component#sendAction
was the only way for a component to call an
action on a parent scope. In 1.13 with the so called closure actions a more intuitive and flexible
way of calling actions was introduced, yielding the old way redundant.
Motivation
With the new closure actions being the recommended way, component#sendAction
is not even
mentioned in the guides.
With the goal of simplifying the framework I think we should remove what is not considered the
current best practice.
Closure actions have been available since 1.13. That is 3 years ago, so deprecating sendAction
should not cause too much pain and yet addons can support still support the last version of the 1.X
cycle if they really want to.
It is out of the scope of this RFC to enumerate the reasons why closure actions are preferred over sendAction but you can find an in depth explanation of closure actions in this blog post from 2016.
Detailed design
A deprecation message will appear when sendAction
is invoked. The feature will be removed in
Ember 4.0. The deprecation message will use the arguments passed to sendAction
to generate a dynamic
explanation that will make super-easy for developers to migrate to closure actions.
As it is mandatory with new deprecations, a new entry in the deprecation guides will be added explaining the migration path in depth.
To refresh what the migration path would look like in the typical use case.
BEFORE
// parent-component.js
export default Component.extend({
actions: {
sayHi() {
alert('Hello user!');
}
}
})
{{!-- parent-component.hbs --}}
{{child-component salute="sayHi"}}
// child-component.js
export default Component.extend({
actions: {
sendSalute() {
this.sendAction('salute');
}
}
});
{{!-- child-component.hbs --}}
<button {{action "sendSalute"}}>Send salute</button>
AFTER
// parent-component.js
export default Component.extend({
actions: {
sayHi() {
alert('Hello user!');
}
}
})
{{!-- parent-component.hbs --}}
{{child-component salute=(action "sayHi")}}
// child-component.js
export default Component.extend({
actions: {
sendSalute() {
this.salute();
// if the salute action is optional you'll have to guard in case it's undefined:
// if (this.salute) {
// this.salute()
// }
//
// Alternatively, you can also define a noop salute function:
// salute() {}
//
// This allows you to remove the guard while provinding an obvious place to add
// docs for that action.
}
}
});
{{!-- child-component.hbs --}}
<button {{action "sendSalute"}}>Send salute</button>
However closure actions allow to be less verbose, so the same behavior could be attained using less intermediate calls
// parent-component.js
export default Component.extend({
actions: {
sayHi() {
alert('Hello user!');
}
}
})
{{!-- parent-component.hbs --}}
{{child-component salute=(action "sayHi")}}
{{!-- child-component.hbs --}}
<button onclick={{@salute}}>Send salute</button>
How we teach this
There are no new concepts to teach, but the removal of an old concept now considered outdated.
Drawbacks
There might be some churn following the deprecation, specially comming from addons that haven't been updated in a while. Addons that want to support the latest versions of Ember without deprecation messages and still work past Ember 1.13 will have to do some gymnastics to do so.
Alternatives
Wait longer to deprecate it and keep sendAction
undocumented until it's usage is yet more minoritary
than it is today, to lower the churn.