Angle Bracket Invocations For Built-in Components
Summary
RFC #311 introduced the angle bracket component invocation syntax. Many developers in the Ember community have since adopted this feature with very positive feedback. This style of component invocation will become the default style in the Octane edition and become the primary way component invocations are taught.
However, Ember ships with three built-in components – {{link-to}}
, {{input}}
and {{textarea}}
. To date, it is not possible to invoke them with the angle
bracket syntax due to various API mismatches and implementation details.
This RFC proposes some small amendments to these APIs and their implementations
to allow them to be invoked with the angle bracket syntax, i.e. <LinkTo>
,
<Input>
and <Textarea>
.
Motivation
As mentioned above, this will allow Ember developers to invoke components with a consistent syntax, which should make it easier to teach.
This RFC does not aim to "fix" issues or quirks with the existing APIs – it merely attempts to provide a way to do the equivalent invocation in angle bracket syntax.
Detailed design
<LinkTo>
There are two main problem with {{link-to}}
:
- It uses positional arguments as the main API.
- It supports an "inline" form (i.e. without a block).
In the new world, components are expected to work with named arguments. This is
both to improve clarity and to match the HTML tags model (which angle bracket
invocations are loosely modelled after). Positional arguments are reserved for
"control-flow-like" components (e.g. liquid-if
) and to be paired with the
curly bracket invocation style. Since links are not that, it is not appropiate
for this component to use positional params.
When invoked with a block, the first argument is the route to navigate to. We
propose to name this argument explicitly, with @route
:
{{#link-to "about"}}About Us{{/link-to}}
...becomes...
<LinkTo @route="about">About Us</LinkTo>
The second argument can be used to provide a model to the route. We propose to
name this argument explicitly, with @model
:
{{#let this.model.posts.firstObject as |post|}}
{{#link-to "post" post}}Read {{post.title}}...{{/link-to}}
...becomes...
<LinkTo @route="post" @model={{post}}>Read {{post.title}}...</LinkTo>
{{/let}}
In fact, it is possible to pass multiple models to deeply nested routes with
additional positional arguments. For this use case, we propose the @models
named argument which accepts an array:
{{#let this.model.posts.firstObject as |post|}}
{{#each post.comments as |comment|}}
{{#link-to "post.comment" post comment}}
Comment by {{comment.author.name}} on {{comment.date}}
{{/link-to}}
...becomes...
<LinkTo @route="post.comment" @models={{array post comment}}>
Comment by {{comment.author.name}} on {{comment.date}}
</LinkTo>
{{/each}}
{{/let}}
The singular @model
argument is a special case of @models
, provided as a
convenience for the common case. Passing both @model
and @models
will be an
error. Passing insufficient amount of models for the given route, will continue
to be an error.
It is also possible to pass query params to the {{link-to}}
component with
the somewhat awkward (query-params)
API. We propose to replace it with a
@query
named argument that simply take a regular hash (or POJO):
{{#link-to "posts" (query-params direction="desc" showArchived=false)}}
Recent Posts
{{/link-to}}
...becomes...
<LinkTo @route="posts" @query={{hash direction="desc" showArchived=false}}>
Recent Posts
</LinkTo>
Finally, as mentioned above, {{link-to}}
supports an "inline" form without a
block. This form doesn't bring much value and causes confusion around
the ordering of the arguments. We propose to simply not support this for the
angle bracket invocation style:
{{link-to "About Us" "about"}}
...becomes...
<LinkTo @route="about">About Us</LinkTo>
Other APIs of this compoment are already based on named arguments.
Migration Path
We would provide a codemod to convert the old invocation style into the new style.
Template Lints
Even though the angle bracket invocation style is recommended going forward,
components can generally be invoked using the either the curly or angle bracket
syntax. Therefore, while not recommended, {{link-to}}
would still work and
invoke the same component.
We propose to add template lint rules to using this component with the curly invocation style.
<Input>
Today, the {{input}}
component is internally implemented as several internal
components that are selected based on the type
argument. This is intended as
an internal implementation detail, but as a result, it is not possible to invoke
the component with <Input>
since it does not exist as a "real" component.
We propose to change this internal implementation strategy to make it possible to invoke it with angle brackets just like any other components.
For example:
{{input type="text" value=this.model.name}}
...becomes...
<Input @type="text" @value={{this.model.name}} />
Another example:
{{input type="checkbox" name="email-opt-in" checked=this.model.emailPreference}}
...becomes...
<Input @type="checkbox" @name="email-opt-in" @checked={{this.model.emailPreference}} />
Migration Path
We would provide a codemod to convert the old invocation style into the new style.
Template Lints
Even though the angle bracket invocation style is recommended going forward,
components can generally be invoked using the either the curly or angle bracket
syntax. Therefore, while not recommended, {{input}}
would still work and
invoke the same component.
We propose to add template lint rules to using this component with the curly invocation style.
<Textarea>
Due to a similar implementation issue, it is also not possible to invoke the
{{textarea}}
component with angle bracket invocation style.
We propose to change this internal implementation strategy to make it possible to invoke this with angle brackets just like any other components.
For example:
{{textarea value=this.model.body}}
...becomes...
<Textarea @value={{this.model.body}} />
Migration Path
We would provide a codemod to convert the old invocation style into the new style.
RFC #176 picked text-area
/TextArea
for
this component. To prevent confusion, we will add a helpful hint to the error
message ("did you mean <Textarea>
?") when a user mistakenly typed
{{text-area}}
or <TextArea>
in development mode.
Template Lints
Even though the angle bracket invocation style is recommended going forward,
components can generally be invoked using the either the curly or angle bracket
syntax. Therefore, while not recommended, {{textarea}}
would still work and
invoke the same component.
We propose to add template lint rules to using this component with the curly invocation style.
How we teach this
Going forward, we will focus on teaching the angle bracket invocation style as the main (only?) way of invoking components. In that world, there wouldn't be anything extra to teach, as the invocation style proposed in this RFC is not different from any other components, which is the purpose of this proposal. Of course, the APIs of these components will still need to be taught, but that is not a new change.
The only caveat is that, since the advanced <LinkTo>
APIs require passing
arrays and hashes, the {{array}}
and {{hash}}
helper would have to be
taught before those advanced features can be introduced. However, since the
basic usage (linking to top-level routes) does not require either of those
helpers, it doesn't really affect things from a getting started perspective.
It should also be mentioned that, other built-ins, such as {{yield}}
,
{{outlet}}
, {{mount}}
, etc are considered "keywords" not components, they
are also "control-flow-like", so it wouldn't be appropiate to invoke them with
angle brackets.
The technical implementation of this RFC will need to be accompanied by changes to the API docs for the built-in template helpers, link-to and input. In keeping with past decisions for Angle Brackets, the API docs should show both curly and Angle Bracket invocations of these helpers. The API docs are expected to show the full supported API surface of Ember.
link-to
and input
are used liberally throughout the Ember.js Guides, Tutorial, and super-rentals
sample app, so those examples will need to be updated. In the Guides, we want to show solely Angle Brackets invocation. The syntax conversion guide should be revised to include these helpers.
Drawbacks
None.
Alternatives
<LinkTo>
could only support@models
without special casing@model
as a convenience.<LinkTo>
could support a@text
argument for inline usage.<Textarea>
could be named<TextArea>
.
Unresolved questions
None.