Prior to Ember 4.0, Ember's template syntax included a feature called "This Fallback."
This meant that Ember would interpret a bare variable name like hello
as this.hello
under certain circumstances.
- this fallback
- The feature described in this document, which causes Ember to interpret
variableName
asthis.variableName
under certain circumstances. This feature was removed in Ember 4.0. - variable reference
- A valid Handlebars variable name (such as
hello
,varName
, orhello-world
) - local variable reference
-
A variable reference that refers to a Handlebars local variable binding (created using
as |varName|
). this
refererence- A variable reference that refers to the current
this
context of the template. - Named Argument Reference ("at-name")
- A variable reference that refers to a named argument using
@{identifier}
syntax. - Ambiguous variable reference
- A variable reference that is not a local variable reference, a
this
reference, or a named argument reference. - Property Path
- A dot-separated path that begins with a variable reference. This variable reference segment is a candidate for this fallback behavior under the circumstances described in this document.
Important
Only ambiguous variable references are candidates for this fallback behavior.
This excludes:
- local variable reference
- this reference
- named argument reference
The lexical scope of a classic Handlebars template has:
- Local variables, bound with
as |var-name|
and referenced via path expressions (var-name
orvar-name.property...
). These variables are in thelocal
namespace. - Global variable references, in one of the following namespaces:
html
component
helper
modifier
value
ambiguous::content
ambiguous::attr-value
keyword
(such asyield
,if
, etc.)
Important
The namespaces are determined by the parser and are purely syntactic. For the most part, they map onto straight-forward user intuitions about what the syntax means. The ambiguous
namespaces are an exception: they are ambiguous from a human understanding perspective, and this fallback behjavior makes them worse.
Global variables in the value namespaces have this fallback behavior (in Ember 3.28), as do global variables in the ambiguous namespaces.
No other namespaces have this fallback behavior.
Global variable references in the value
and ambiguous
namespaces have this fallback behavior in Ember 3.28, but it was removed in Ember 4.0.
Note
Variable references with this fallback behavior are annotated with ^^^
.
These examples are equivalent to this template, with namespaces made explicit (and prefixed with %
):
Note
These resolution rules exist in the parser, in largely this form. The elaborated syntax was created for this explanation and doesn't exist elsewhere (yet?).
In this example, variable references with this fallback behavior are annotated with ^^^
and their associated namespace is annotated with ~~~
.
The basic intuition of this fallback behavior is:
When a piece of syntax that could be a path expression is used as a value, and the variable at the front of the path is not in local Handlebars scope, it falls back to
this.var-name
if necessary.
This fallback behavior happens far less frequently than you might expect, and when it happens, the consequences are fairly constrained.
This is because:
- It doesn't apply to unambiguous syntax like
<ComponentName>
,{{modifier-name}}
or(helper-name with args)
. - When it applies to arguments, this fallback behavior is the only option after ruling out local variables.
- When it applies to ambiguous syntax, it only applies after determining that there is no component or helper with that name.
This means that it applies to only these scenarios, and only if var-name
is not in local scope:
- In content position (
{{var-name}}
), if there is no component or helper namedvar-name
, and only if there are zero arguments. - In attribute value position (
attr={{var-name}}
), if there is no helper namedvar-name
, and only if there are zero arguments. - In any argument position (
(some-helper this or.this or=even.this)
). But since this only applies if the variable reference is not in scope, it's possible to determine, completely statically, when the rule applies. TL;DR it only applies if the variable is not in local scope, and the only possible fix is to prefixthis.
.
The behavior of this fallback is different depending on the syntactic position. This section describes the relevant differences.
Namespace | component |
This fallback behavior | Never |
An HTML tag name that syntactically meets one of these criteria is a Glimmer Property Path.
- begins with a capital letter
- is a path that begins with local variable reference
- is a path with at least two segments (i.e. contains a dot)
Important
None of these variable references are candidates for this fallback behavior.
{{!--
⛔️ None of these examples are candidates for this fallback behavior ⛔️
}}
{{! the path is `ComponentName`}}
<ComponentName />
{{! the path is `f.input` and the variable reference is `f`}}
<f.input />
{{! the path is @arg}}
<@arg />
{{! the path is @arg.property and the variable reference is `@arg`}}
<@arg.property />
{{#let @arg.property as |comp|}}
{{! the path is comp}}
<comp />
{{! the path is comp.child and the variable reference is `comp`}}
<comp.child>
{{/let}}
Namespace | modifier |
This fallback behavior | Never |
The callee of a modifier invocation is always a Glimmer Property Path.
Important
None of these variable references are candidates for this fallback behavior.
It doesn't matter whether the modifier has any arguments.
{{!--
⛔️ None of these examples are candidates for this fallback behavior ⛔️
}}
{{! the path is `modifier-name`}}
<div {{modifier-name}} />
{{! the path is `name.modifier` and the variable reference is `name`}}
<div {{name.modifier}} />
{{! the path is @arg}}
<@arg />
{{! the path is @arg.property and the variable reference is `@arg`}}
<@arg.property />
{{#let @arg.property as |comp|}}
{{! the path is comp}}
<comp />
{{! the path is comp.child and the variable reference is `comp`}}
<comp.child>
{{/let}}
Namespace | helper |
This fallback behavior | Never |
When a syntax is unambiguously a helper invocation, its callee is always a Glimmer Property Path.
A syntax is unambiguously a helper invocation if it meets one of the following criteria:
- It is the callee in a subexpression (i.e.
(helper-name)
or(helper-name arg)
, regardless of whether the subexpression has any arguments. - It is the callee in
{{curly syntax}}
and it has at least one positional or named argument. This includes attribute values.
Tip
The intuition is: "it looks like a call expression and isn't an [ambiguous invocation][#ambiguous-invocation].
Namespace | ambiguous:content |
This fallback behavior | If the variable name is not a global helper or component |
Ambiguous content:
- appears in content position
- consistents of only a single ambiguous variable reference (the ambiguous name), and has no arguments of any kind.
- is not an Ember content keyword such as
{{yield}}
.
If the ambiguous name is var-name
, this syntax ({{var-name}}
in content position) could mean any of the following:
- Invoke a helper named
var-name
- Invoke a component named
var-name
(this fallback) Look up(removed in Ember 4.0)this.var-name
Namespace | ambiguous:attr |
This fallback behavior | If the variable name is not a global helper |
An ambiguous attribute value:
- appears in attribute value position
- consistents of only a single ambiguous variable reference (the ambiguous name), and has no arguments of any kind.
- is not an Ember helper keyword (such as
if
).
If the ambiguous name is var-name
, this syntax (attr={{var-name}}
in content position) could mean:
- Invoke a helper named
var-name
(this fallback) Look up(removed in Ember 4.0)this.var-name
[!NOTE] In Ember 4.0, this syntax is unambiguous.
Namespace | value |
This fallback behavior | Always |
Tip
Intuitively, an argument position is an argument passed to any kind of invocation.
The argument position syntax is any of these:
- Positional arguments in all curly syntaxes (components, helpers, modifiers, content, etc.) and subexpressions.
- Argument values passed as named arguments in all curly syntaxes and subexpressions.
- Argument values passed as named arguments via angle-bracket component invocations.
[!INFO]
The variable reference is annotated via
^^^
and the rest of the path expression is annotated via~~~
.