Deprecate {{with}}
Summary
The {{with}}
helper has always had slightly confusing conditional semantics, this was one of the motivators for introducing the easier to understand {{let}}
helper. Now that {{let}}
exists, the remaining use case for using {{with}}
is for its unique conditional semantics. These conditional semantics can be cleanly represented with a combination of {{let}}
and {{if}}
statements so we should deprecate {{with}}
.
Motivation
The difference between {{let}}
and {{with}}
is with how they handle conditional arguments. The {{let}}
helper's block content is always rendered, regardless of its parameters. In contrast, {{with}}
only renders its main block when the first position parameter is truthy. For example:
{{#with "Alex" as |value|}}
{{value}} is truthy
{{else}}
The first positional param was falsy
{{/with}}
Will render "[Alex] is truthy".
{{#with false as |value|}}
{{value}} is truthy
{{else}}
The first positional param was falsy
{{/with}}
Will render "The first positional param was falsy".
The conditional arguments behavior of {{with}}
can easily be replicated using a combination of {{let}}
and {{if}}
in a way that's easily readable:
{{#let "Alex" as |value|}}
{{#if value}}
{{value}} is truthy
{{else}}
The first positional param was falsy
{{/if}}
{{/let}}
Detailed design
We'll create an AST transform in packages/ember-template-compiler
which will emit a deprecation warning for all uses of {{with}}
. The deprecation warning will be:
Using `{{with}}` is deprecated, please use `{{let}}` instead.
This message will link to the following deprecation details which aim to give clear guidance on how to migrate to using {{let}}
, {{if}}
and {{else}}
in different combinations:
The use of {{with}}
has been deprecated, you should replace it with either {{let}}
or a combination of {{let}}
, {{if}}
and {{else}}
:
If you always want the block to render, replace {{with}}
with {{let}}
directly:
Before:
{{#with (hash name="Ben" age=4) as |person|}}
Hi {{person.name}}, you are {{person.age}} years old.
{{/with}}
After:
{{#let (hash name="Ben" age=4) as |person|}}
Hi {{person.name}}, you are {{person.age}} years old.
{{/let}}
If you want to render a block conditionally, use a combination of {{let}}
and {{if}}
:
Before:
{{#with user.posts as |blogPosts|}}
There are {{blogPosts.length}} blog posts
{{/with}}
After:
{{#let user.posts as |blogPosts|}}
{{#if blogPosts}}
There are {{blogPosts.length}} blog posts
{{/if}}
{{/let}}
If you want to render a block conditionally, and otherwise render an alternative block, use a combination of {{let}}
, {{if}}
and {{else}}
:
Before:
{{#with user.posts as |blogPosts|}}
There are {{blogPosts.length}} blog posts
{{else}}
There are no blog posts
{{/with}}
After:
{{#let user.posts as |blogPosts|}}
{{#if blogPosts}}
There are {{blogPosts.length}} blog posts
{{else}}
There are no blog posts
{{/if}}
{{/let}}
For people on older versions of Ember that support {{let}}
, we'll create an ember-template-lint
rule that they can use to prevent the use of {{with}}
.
We'll also create a codemod which will assist people when migrating from {{with}}
to {{let}}
.
How we teach this
We'll mentiton the deprecation in an Ember point release blog post.
As mentioned above, the deprecation message will contain a link to clear guidelines on how to migrate to using {{let}}
.
There is nothing to remove from the Ember.js Guides since we already teach only the use of {{let}}
.
Drawbacks
This adds a little churn to Ember's API.
Alternatives
We could leave {{with}}
as is. I don't believe that this is a good option as the name {{with}}
is confusing.
We could deprecate {{with}}
and introduce {{if-let}}
in Ember core. This RFC originally made that exact proposal, I was strongly persuaded of the lack of need for {{if-let}}
by @tcjr.
We could deprecate {{with}}
and introduce {{if-let}}
in an addon instead of Ember core.
Unresolved questions
(none)