-
-
Notifications
You must be signed in to change notification settings - Fork 408
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Angle Bracket Invocations For Built-in Components #459
Merged
Merged
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
2216eec
Angle Bracket Invocations For Built-in Components
chancancode f3b2453
Update text/0459-angle-bracket-built-in-components.md
rwjblue d60495f
Update text/0459-angle-bracket-built-in-components.md
rwjblue d3a0e4d
typo
chancancode 9abd68d
framework core team feedback
chancancode 0ebfbb3
Update 0459-angle-bracket-built-in-components.md
locks 32a25b3
Update text/0459-angle-bracket-built-in-components.md
jenweber File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,269 @@ | ||
- Start Date: 2019-03-05 | ||
- Relevant Team(s): Ember.js | ||
- RFC PR: https://github.com/emberjs/rfcs/pull/459 | ||
- Tracking: (leave this empty) | ||
|
||
# Angle Bracket Invocations For Built-in Components | ||
|
||
## Summary | ||
|
||
[RFC #311](./0311-angle-bracket-invocation.md) 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`: | ||
|
||
```hbs | ||
{{#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`: | ||
|
||
```hbs | ||
{{#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: | ||
|
||
```hbs | ||
{{#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): | ||
|
||
```hbs | ||
{{#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: | ||
|
||
```hbs | ||
{{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: | ||
|
||
```hbs | ||
{{input type="text" value=this.model.name}} | ||
|
||
...becomes... | ||
|
||
<Input @type="text" @value={{this.model.name}} /> | ||
``` | ||
|
||
Another example: | ||
|
||
```hbs | ||
{{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: | ||
|
||
```hbs | ||
{{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](./0176-javascript-module-api.md) 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. | ||
|
||
chancancode marked this conversation as resolved.
Show resolved
Hide resolved
|
||
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](https://emberjs.com/api/ember/3.8/classes/Ember.Templates.helpers/methods/link-to?anchor=link-to) and [input](https://emberjs.com/api/ember/3.8/classes/Ember.Templates.helpers/methods/input?anchor=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](https://github.com/ember-learn/super-rentals), so those examples will need to be updated. In the Guides, we want to show solely Angle Brackets invocation. The [syntax conversion guide](https://guides.emberjs.com/release/reference/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. |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we were building these components from first principals, would we choose this API? Might this be an opportunity to build something better, removing
type
and the overloaded input API and instead provide dedicated components such as<TextInput />
and<Checkbox />
? (with possibly better names)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think there are advantages sticking close to underlying HTML element that it is trying to bridge (even though we don't fully support all the
input
types too). Also, this RFC just tries to fix a narrow problem (provide a 1:1 transition path from the curly invocations) without opening the can of worms of "hey.. what if we also just fix this one thing......", they can be explored in separate RFCs IMO, so long as they are codemod-able I don't think there is a lot of extra cost to do them later.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 thanks, agreed. Having the opportunity to codemod our way to some possible future API means that this is a nice incremental improvement
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, but it is not this RFCs intent to "fix the design of the underlying components", that should be done separately.
tldr; what Godfrey said 😸
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the same point I came here to make. In particular, given
...attributes
and the semantics oftype
vs.@type
on a regular<input>
, it seems like it might be worth deprecating{{input}}
(along the lines proposed here) and using the internal mechanics which create the correct input type to offer good suggestions. "You attempted to create a checkbox with{{input}}
; we recommend<CheckBox />
..." etc.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think both could work as @rwjblue points out this RFC is to bridge the gap with the commonly used components today. For instance if a Ember dev reads the guide materials on curly vs angle bracket and sees
{{input type="number"}}
being used it's a much smoother path to change it to<Input @type="number">
vs having to know<TextInput type="number" />
or all of the different variations.A more angle bracket 1st set of input components would be great! We could even use codemods or build time AST transforms to rewrite to these new components in the future. But, for now let's make a way to use the built-in components we have.