|
| 1 | +- Start Date: 2017-12-10 |
| 2 | +- RFC PR: https://github.com/emberjs/rfcs/pull/276 |
| 3 | +- Ember Issue: https://github.com/emberjs/ember.js/pull/15968 |
| 4 | + |
| 5 | +# Summary |
| 6 | + |
| 7 | +Introduce `{{@foo}}` in as a dedicated syntax for a component's template to |
| 8 | +refer to named arguments passed in by the caller. |
| 9 | + |
| 10 | +For example, given the invocation `{{hello-world name="Godfrey"}}` and this |
| 11 | +component template in `app/templates/components/hello-world.hbs`: |
| 12 | + |
| 13 | +```hbs |
| 14 | +Hello, {{@name}} |
| 15 | +``` |
| 16 | + |
| 17 | +Ember will render "Hello, Godfrey". |
| 18 | + |
| 19 | +# Motivation |
| 20 | + |
| 21 | +Currently, the way to access named arguments passed in from the caller is to |
| 22 | +reference `{{name}}` in the template. This works because when Ember creates |
| 23 | +the component instance, it automatically [assigns](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign) |
| 24 | +all named arguments as properties on the component instance. |
| 25 | + |
| 26 | +The first problem with this approach is that the `{{name}}` syntax is highly |
| 27 | +ambigious, as it could be referring to a local variable (block param), a |
| 28 | +helper or a named argument from the caller (which actually works by accessing |
| 29 | +auto-reflected `{{this.name}}`) or a property on the component class (such as |
| 30 | +a computed property). |
| 31 | + |
| 32 | +This can often lead to confusion for readers of the template. Upon encountering |
| 33 | +`{{foo}}` in a component's template, the reader has to check all of |
| 34 | +these places: first you need to scan the surrounding lines for block |
| 35 | +params with that name; next you check in the helpers folder to see it there |
| 36 | +is a helper with that name (it could also be coming from an addon!); then you |
| 37 | +check if it is an argument provided by the caller; finally, you check the |
| 38 | +component's JavaScript class to look for a (computed) property. If you _still_ |
| 39 | +did not find it, maybe it is a named arguments that is passed only sometimes, |
| 40 | +or perhaps it is just a leftover reference from a previous refactor? |
| 41 | + |
| 42 | +Providing a dedicated syntax for referring to named arguments will resolve the |
| 43 | +ambiguity and greatly improve clarity, especially in big projects with a lot |
| 44 | +of files (and uses a lot of addons). (The existing `{{this.name}}` syntax can |
| 45 | +already be used to disambiguate component properties from helpers.) |
| 46 | + |
| 47 | +As an aside, the ambiguity that causes confusion for human readers is also a |
| 48 | +problem for the compiler. While it is not the main goal of this proposal, |
| 49 | +resolving this ambiguity also helps the rendering system. Currently, the |
| 50 | +"runtime" template compiler has to perform a helper lookup for every `{{name}}` |
| 51 | +in each template. It will be able to skip this resolution process and perform |
| 52 | +other optimizations (such as reusing the internal [reference](https://github.com/glimmerjs/glimmer-vm/blob/master/guides/04-references.md) |
| 53 | +object and caches) with this addition. |
| 54 | + |
| 55 | +Another problem with the current approach of automatically "reflecting" named |
| 56 | +arguments on the instance is that they can unexpectedly overwrite other |
| 57 | +properties defined on the component's class. It also defeats performance |
| 58 | +optimizations in JavaScript engines as this approach creates many different |
| 59 | +polymorphic "shapes" for instances that otherwise belong to the same |
| 60 | +component class. |
| 61 | + |
| 62 | +While this proposal does not directly solve this problem (we are not proposing |
| 63 | +that we deprecate or remove the "auto-reflection" on `Ember.Component`), it |
| 64 | +paves the way for a future world where components can work without them. |
| 65 | + |
| 66 | +Notably, the current iteration of the [Glimmer Components](https://glimmerjs.com/guides/templates-and-helpers) |
| 67 | +have adopted this design for over a year now and the experience has been very |
| 68 | +positive. This would be one of the first pieces (admittedly, only a tiny piece) |
| 69 | +of the Glimmer.js experiment to make its way into Ember. We think this feature |
| 70 | +is small, self-contained but useful enough to be the ideal candidate to kick |
| 71 | +off this process. |
| 72 | + |
| 73 | +# Detailed design |
| 74 | + |
| 75 | +This feature was baked into the Glimmer VM very early on. In fact, the |
| 76 | +only thing that is stopping them from working in Ember is [an AST transform](https://github.com/emberjs/ember.js/blob/87be17d8e69f83b2abed8c0695f8fa5e4bcae473/packages/ember-template-compiler/lib/plugins/assert-reserved-named-arguments.js) |
| 77 | +that specifically disallows them. Therefore, "implementing" this feature is |
| 78 | +just a matter of deleting that file. |
| 79 | + |
| 80 | +Additionally, the legacy `{{attrs.foo}}` syntax (which more or less tries to |
| 81 | +accomplish the same thing) has actually been [implemented using `{{@foo}}`](https://github.com/emberjs/ember.js/blob/87be17d8e69f83b2abed8c0695f8fa5e4bcae473/packages/ember-template-compiler/lib/plugins/transform-attrs-into-args.js) |
| 82 | +under-the-hood since Ember 2.10. |
| 83 | + |
| 84 | +## Reserved Names |
| 85 | + |
| 86 | +We will reserve `{{@args}}`, `{{@arguments}}` and anything that does not |
| 87 | +start with a lowercase letter (such as `@Foo`, `@0`, `@!` etc) in the first |
| 88 | +version. This is purely speculative and the goal is to carve out some space |
| 89 | +for future features. If we don't end up needing them, we can always relax |
| 90 | +the restrictions down the road. |
| 91 | + |
| 92 | +# How We Teach This |
| 93 | + |
| 94 | +`{{@foo}}` is the way to access the named arguments passed from the caller. |
| 95 | + |
| 96 | +Since the `{{foo}}` syntax still works on `Ember.Component` (which is the |
| 97 | +only kind of components available today) via the auto-reflection mechanism, |
| 98 | +we are not really in a rush to migrate the community (and the guides, etc) |
| 99 | +to using the new syntax. In the meantime, this could be viewed as a tool to |
| 100 | +improve clarity in templates, similar to how the optional "explicit `this`" |
| 101 | +syntax (`{{this.foo}}`). |
| 102 | + |
| 103 | +While we think writing `{{@foo}}` would be a best practice for new code |
| 104 | +going forward, the community can migrate at its own pace one component at a |
| 105 | +time. |
| 106 | + |
| 107 | +We can also encourage the community to supplement this effort by wiring |
| 108 | +linting tools and code mods. |
| 109 | + |
| 110 | +# Drawbacks |
| 111 | + |
| 112 | +This introduces a new piece of syntax that one would need to learn in order to |
| 113 | +understand Ember templates. |
| 114 | + |
| 115 | +This mostly affects "casual" readers (as this should be very easy for an Ember |
| 116 | +developer to learn, understand and remember after encounting/learning it for |
| 117 | +the first time). However, since these casual readers are also among those |
| 118 | +who are most acutely affected by the ambiguity, we believe this is still a |
| 119 | +net improvement over the status-quo. |
| 120 | + |
| 121 | +# Alternatives |
| 122 | + |
| 123 | +We have `{{attrs.foo}}` today. In React, there is `this.props.foo`. |
| 124 | + |
| 125 | +Given how common this is, we think it deserves its own dedicated, succinct |
| 126 | +syntax. The other alternatives that involve reflecting them on the component |
| 127 | +instances also would not allow for the internal optimizations in the Glimmer |
| 128 | +VM. |
0 commit comments