Skip to content

Commit 2216eec

Browse files
committed
Angle Bracket Invocations For Built-in Components
1 parent 7d46cfa commit 2216eec

1 file changed

Lines changed: 257 additions & 0 deletions

File tree

Lines changed: 257 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,257 @@
1+
- Start Date: 2019-03-05
2+
- Relevant Team(s): Ember.js
3+
- RFC PR: https://github.com/emberjs/rfcs/pull/459
4+
- Tracking: (leave this empty)
5+
6+
# Angle Bracket Invocations For Built-in Components
7+
8+
## Summary
9+
10+
[RFC #311](./0311-angle-bracket-invocation.md) introduced the angle bracket
11+
component invocation syntax. Many developers in the Ember community has since
12+
adopted this feature with very positive feedback. This style of component
13+
invocation will beomce the default style in the Octane edition and become the
14+
primary way component invocations are taught.
15+
16+
However, Ember ships with three built-in components – `{{link-to}}`, `{{input}}`
17+
and `{{textarea}}`. To date, it is not possible to invoke them with the angle
18+
bracket syntax due to various API mismatches and implementation details.
19+
20+
This RFC proposes some small admenments to these APIs and their implementations
21+
to allow them to be invoked with the angle bracket syntax, i.e. `<LinkTo>`,
22+
`<Input>` and `<TextArea>`.
23+
24+
## Motivation
25+
26+
As mentioned above, this will allow Ember developers to invoke components with
27+
a consistent syntax, which should make it easier to teach.
28+
29+
This RFC _does not_ aim to "fix" issues or quirks with the existing APIs – it
30+
merely attempts to provide a way to do the equivilant invocation in angle
31+
bracket syntax.
32+
33+
## Detailed design
34+
35+
### `<LinkTo>`
36+
37+
There are two main problem with `{{link-to}}`:
38+
39+
* It uses positional arguments as the main API.
40+
* It supports an "inline" form (i.e. without a block).
41+
42+
In the new world, components are expected to work with named arguments. This is
43+
both to improve clarity and to match the HTML tags model (which angle bracket
44+
invocations are loosely modelled after). Positional arguments are reserved for
45+
"control-flow-like" components (e.g. `liquid-if`) and to be paired with the
46+
curly bracket invocation style. Since links are not that, it is not appropiate
47+
for this component to use positional params.
48+
49+
When invoked with a block, the first argument is the route to navigate to. We
50+
propose to name this argument explicitly, with `@route`:
51+
52+
```hbs
53+
{{#link-to "about"}}About Us{{/link-to}}
54+
55+
...becomes...
56+
57+
<LinkTo @route="about">About Us</LinkTo>
58+
```
59+
60+
The second argument can be used to provide a model to the route. We propose to
61+
name this argument explicitly, with `@model`:
62+
63+
```hbs
64+
{{#let this.model.posts.firstObject as |post|}}
65+
66+
{{#link-to "post" post}}Read {{post.title}}...{{/link-to}}
67+
68+
...becomes...
69+
70+
<LinkTo @route="post" @model={{post}}>Read {{post.title}}...</LinkTo>
71+
72+
{{/let}}
73+
```
74+
75+
In fact, it is possible to pass multiple models to deeply nested routes with
76+
additional positional arguments. For this use case, we propose the `@models`
77+
named argument which accepts an array:
78+
79+
```hbs
80+
{{#let this.model.posts.firstObject as |post|}}
81+
{{#each post.comments as |comment|}}
82+
83+
{{#link-to "post.comment" post comment}}
84+
Comment by {{comment.author.name}} on {{comment.date}}
85+
{{/link-to}}
86+
87+
...becomes...
88+
89+
<LinkTo @route="post.comment" @models={{array post comment}}>
90+
Comment by {{comment.author.name}} on {{comment.date}}
91+
</LinkTo>
92+
93+
{{/each}}
94+
{{/let}}
95+
```
96+
97+
The singular `@model` argument is a special case of `@models`, provided as a
98+
convenience for the common case. Passing both `@model` and `@models` will be an
99+
error. (So would passing insufficient amount of models for the given route, as
100+
it already is today.)
101+
102+
It is also possible to pass query params to the `{{link-to}}` component with
103+
the somewhat awkward `(query-params)` API. We propose to replace it with a
104+
`@query` named argument that simply take a regular hash (or POJO):
105+
106+
```hbs
107+
{{#link-to "posts" (query-params direction="desc" showArchived=false)}}
108+
Recent Posts
109+
{{/link-to}}
110+
111+
...becomes...
112+
113+
<LinkTo @route="posts" @query={{hash direction="desc" showArchived=false}}>
114+
Recent Posts
115+
</LinkTo>
116+
```
117+
118+
Finally, as mentioned above, `{{link-to}}` supports an "inline" form without a
119+
block. This form doesn't bring much value and used to cause confusion around
120+
the ordering of the arguments. We propose to simply not support this for the
121+
angle bracket invocation style:
122+
123+
```hbs
124+
{{link-to "About Us" "about"}}
125+
126+
...becomes...
127+
128+
<LinkTo @route="about">About Us</LinkTo>
129+
```
130+
131+
Other APIs of this compoment are already based on named arguments.
132+
133+
#### Migration Path
134+
135+
We would provide a codemod to convert the old invocation style into the new
136+
style.
137+
138+
#### Deprecations
139+
140+
Even though the angle bracket invocation style is recommended going forward
141+
(and should be enforced via a template lint), components can generally be
142+
invoked using the either the curly or angle bracket syntax. Therefore, while
143+
not recommended, `{{link-to}}` would still work and invoke the same component.
144+
145+
In the case that the curly invocation style is used, we propose to issue the
146+
following deprecation warnings:
147+
148+
* Passing positional arguments to `{{link-to}}` (pass named arguments instead)
149+
* Using the inline form of `{{link-to}}` (use the block form instead)
150+
* The `query-params` helper (pass a hash/POJO to the `query` argument instead)
151+
152+
### `<Input>`
153+
154+
Today, the `{{input}}` component is internally implemented as several internal
155+
components that are selected based on the `type` argument. This is intended as
156+
an internal implmentation detail, but as a result, it is not possible to invoke
157+
the component with `<Input>` since it does not exist as a "real" component.
158+
159+
We propose to change this internal implementation strategry to make it possible
160+
to invoke this with angle brackets just like any other components.
161+
162+
For example:
163+
164+
```hbs
165+
{{input type="text" value=this.model.name}}
166+
167+
...becomes...
168+
169+
<Input @type="text" @value={{this.model.name}} />
170+
```
171+
172+
Another example:
173+
174+
```hbs
175+
{{input type="checkbox" name="email-opt-in" checked=this.model.emailPreference}}
176+
177+
...becomes...
178+
179+
<Input @type="checkbox" @name="email-opt-in" @checked={{this.model.emailPreference}} />
180+
```
181+
182+
#### Migration Path
183+
184+
We would provide a codemod to convert the old invocation style into the new
185+
style.
186+
187+
#### Deprecations
188+
189+
None.
190+
191+
### `<TextArea>`
192+
193+
Due to a similar implementation issue, it is also not possible to invoke the
194+
`{{textarea}}` component with angle bracket invocation style.
195+
196+
We propose to change this internal implementation strategry to make it possible
197+
to invoke this with angle brackets just like any other components. However, to
198+
align with the name used in [RFC #176](./0176-javascript-module-api.md), we
199+
propose to take this opportunity to rename the component into `<TextArea>` (as
200+
opposed to `<Textarea>`).
201+
202+
For example:
203+
204+
```hbs
205+
{{textarea value=this.model.body}}
206+
207+
...becomes...
208+
209+
<TextArea @value={{this.model.body}} />
210+
```
211+
212+
#### Migration Path
213+
214+
We would provide a codemod to convert the old invocation style into the new
215+
style.
216+
217+
#### Deprecations
218+
219+
To prevent surprises, `<Textarea>` will also invoke the same component but with
220+
a deprecation warning (use `<TextArea>` instead).
221+
222+
## How we teach this
223+
224+
Going forward, we will focus on teaching the angle bracket invocation style as
225+
the main (only?) way of invoking components. In that world, there wouldn't be
226+
anything extra to teach, as the invocation style proposed in this RFC is not
227+
different from any other components, which is the purpose of this proposal. Of
228+
course, the APIs of these components will still need to be taught, but that is
229+
not a new change.
230+
231+
The only caveat is that, since the advanced `<LinkTo>` APIs require passing
232+
arrays and hashes, the `{{array}}` and `{{hash}}` helper would have to be
233+
taught before those advanced features can be introduced. However, since the
234+
basic usage (linking to top-level routes) does not require either of those
235+
helpers, it doesn't really affect things from a getting started perspective.
236+
237+
It should also be mentioned that, other built-ins, such as `{{yield}}`,
238+
`{{outlet}}`, `{{mount}}`, etc are considered "keywords" not components, they
239+
are also "control-flow-like", so it wouldn't be appropiate to invoke them with
240+
angle brackets.
241+
242+
## Drawbacks
243+
244+
None.
245+
246+
## Alternatives
247+
248+
* `<LinkTo>` could only support `@models` without special casing `@model` as a
249+
convenience.
250+
251+
* `<LinkTo>` could support a `@text` argument for inline usage.
252+
253+
* `<TextArea>` could just be named `<Textarea>`.
254+
255+
## Unresolved questions
256+
257+
None.

0 commit comments

Comments
 (0)