|
| 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