Skip to content

Commit a8f248f

Browse files
authored
Merge pull request ember-learn#572 from ember-learn/feat/glimmer-components
[WIP][OCTANE] Update Components
2 parents 5cb1d46 + 867b726 commit a8f248f

12 files changed

+1638
-1343
lines changed

guides/release/components/actions-and-events.md

Lines changed: 609 additions & 0 deletions
Large diffs are not rendered by default.

guides/release/components/adding-actions.md

Lines changed: 0 additions & 531 deletions
This file was deleted.
Lines changed: 273 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,273 @@
1+
_Arguments_ and _attributes_ are the two kinds of things that you can pass to a
2+
component when you use it in a template. Arguments are values that are prefixed
3+
with the `@` symbol, and attributes are values that are not:
4+
5+
```handlebars
6+
<Tooltip @content="Required Value" class="error"/>
7+
```
8+
9+
In this example, we're passing the `@content` argument and the `class` attribute
10+
to an instance of the `Tooltip` component.
11+
12+
Arguments are _JavaScript values_, which are accessible in both the component
13+
template and its class instance, if a class exists. All kinds of values can be
14+
passed as arguments to a component, and can be consumed and used with JavaScript
15+
code.
16+
17+
Attributes, by contrast, are specifically _HTML attributes_, such as `class`,
18+
`id`, `data-test`, and `role`. These get reflected directly onto HTML elements,
19+
and are not accessible directly in either the template of a component, or its
20+
class. Instead, components can direct where they are applied with the
21+
`...attributes` syntax.
22+
23+
## Arguments
24+
25+
Arguments are values that you pass down to your component, and that can be
26+
accessed in the template with the `@` symbol. If we define a `Tooltip`
27+
component, we can pass a `@content` argument to it like so:
28+
29+
```handlebars
30+
<Tooltip @content="Required Value" />
31+
```
32+
33+
And then we can access it in the template for the `Tooltip` component like this:
34+
35+
```handlebars {data-filename=src/ui/components/tooltip/template.hbs}
36+
{{@content}}
37+
```
38+
39+
The same symbol `@` is used on both sides, so its easy to remember that this is
40+
an argument coming from the caller whenever you're looking at the template. In
41+
the component class you can access the arguments on the `args` property of the
42+
class, since `@` is special character in JavaScript reserved for decorators:
43+
44+
```js {data-filename=src/ui/components/tooltip/component.js}
45+
export default class Tooltip extends Component {
46+
get upperCased() {
47+
return this.args.content.toUpperCase();
48+
}
49+
}
50+
```
51+
52+
In templates arguments are valid identifiers that can be used anywhere a
53+
standard identifier can:
54+
55+
```handlebars
56+
{{!-- in a helper --}}
57+
{{capitalize @content}}
58+
59+
{{!-- in an attribute --}}
60+
<div class="{{@class}}"></div>
61+
62+
{{!-- as an argument --}}
63+
<Icon @type={{@iconType}} />
64+
```
65+
66+
You can pass strings as arguments to components, or you can pass literal values
67+
using double curlies:
68+
69+
```handlebars
70+
<Tooltip
71+
@icon="warning"
72+
@count={{4}}
73+
@isError={{false}}
74+
/>
75+
```
76+
77+
Note that if you do _not_ wrap literal values in double curlies, they are
78+
treated as strings, like standard HTML attributes:
79+
80+
```handlebars
81+
<Tooltip @isError=false />
82+
83+
{{!-- is equivalent to... --}}
84+
<Tooltip @isError="false" />
85+
```
86+
87+
You can also pass a class property, or the result of a helper, through
88+
arguments:
89+
90+
```handlebars
91+
<Tooltip
92+
@icon={{this.icon}}
93+
@isError={{not this.isWarning}}
94+
/>
95+
```
96+
97+
In short, arguments are how you pass _JavaScript values_ into a component. This
98+
is part of why they are named _arguments_ - if you think of using a component
99+
like calling a _function_, whose result is a new component that produces some
100+
HTML, then arguments to a component are the same as arguments to a function.
101+
They're values that you pass to the component that the component then uses to
102+
produce a result (the final HTML).
103+
104+
### Argument Defaults
105+
106+
At some point, you may want to add default values to your arguments if one
107+
wasn't passed to your component. Arguments are not mutable, so if you attempt to
108+
reassign a value on `this.args`, it'll fail. Instead, you should define a getter
109+
on your component that provides the default value if the argument was not
110+
provided.
111+
112+
For instance, if you wanted to create a tooltip icon that had a standard icon
113+
and class, you could do it like so:
114+
115+
```javascript {data-filename=src/ui/components/tooltip/component.js}
116+
import Component from '@glimmer/component';
117+
118+
export default class Tooltip extends Component {
119+
get icon() {
120+
return this.args.icon || 'icon-info';
121+
}
122+
123+
get tooltipClass() {
124+
return this.args.tooltipClass + ' tooltip';
125+
}
126+
}
127+
```
128+
129+
```handlebars {data-filename=src/ui/components/tooltip/template.hbs}
130+
<div class="{{this.tooltipClass}}">
131+
<i class="{{this.icon}}"></i>
132+
{{@content}}
133+
</div>
134+
```
135+
136+
Now when called like so:
137+
138+
```handlebars
139+
<Tooltip @content="I'm a tooltip!"/>
140+
```
141+
142+
The result will be:
143+
144+
```html
145+
<div class="tooltip">
146+
<i class="icon-info"></i>
147+
I'm a tooltip!
148+
</div>
149+
```
150+
151+
Note that because arguments are prefixed with `@` in templates, and placed on
152+
`args` in the component definition, we can use the same name for our `icon` and
153+
`tooltipClass` getters, which is pretty convenient. We can also tell clearly
154+
when we look at the template for the tooltip that `this.tooltipClass` and
155+
`this.icon` are values that come from the class definition, and that means they
156+
probably have been used in some kind of dynamic code (in this case, our
157+
defaulting logic).
158+
159+
### Attributes
160+
161+
While arguments are a way for you to pass JavaScript values into your
162+
components, _attributes_ are about passing HTML attributes like `class`, `id`,
163+
and `role` through your component, down to the final elements that get rendered.
164+
This allows you to easily customize a component without having to add a bunch of
165+
customization logic to every single component.
166+
167+
For instance, in our `<Tooltip>` component from above, instead of adding the
168+
`@tooltipClass` argument, we could use attributes:
169+
170+
```handlebars {data-filename=src/ui/components/tooltip/template.hbs}
171+
<div class="tooltip" ...attributes>
172+
<i class="{{this.icon}}"></i>
173+
{{@content}}
174+
</div>
175+
```
176+
177+
We would then invoke the component like this:
178+
179+
```handlebars
180+
<Tooltip
181+
@content="I'm a tooltip!"
182+
class="warning-tooltip"
183+
/>
184+
```
185+
186+
Then our result is:
187+
188+
```html
189+
<div class="tooltip warning-tooltip">
190+
<i class="icon-info"></i>
191+
I'm a tooltip!
192+
</div>
193+
```
194+
195+
The `...attributes` syntax is how you specify where the attributes should be
196+
applied. It can be applied to elements or components, and you can use it on as
197+
many elements or components within your component as you want:
198+
199+
```handlebars
200+
<BlogPostTitle @title={{title}} ...attributes/>
201+
<BlogPostContent>
202+
<section ...attributes>
203+
<p>
204+
Here's some content!
205+
</p>
206+
</section>
207+
</BlogPostContent>
208+
```
209+
210+
Although typically you'll just want to put it on one of the top level components
211+
or elements. If you don't use `...attributes` in your component and someone
212+
tries to pass an attribute to it, Ember will do nothing, and the attributes will
213+
not be applied.
214+
215+
#### Attribute Order
216+
217+
The positioning of `...attributes` matters, with respect to the other attributes
218+
in the element it is applied to. Attributes that come _before_ `...attributes`
219+
can be overriden, but attributes that come _after_ cannot:
220+
221+
```handlebars
222+
<p
223+
data-overridable="you can override me"
224+
...attributes
225+
data-non-overridable="but you can't override me!"
226+
>
227+
</p>
228+
```
229+
230+
There is one exception to this, which is the `class` attribute. `class` will get
231+
merged, since its more often the case that users of the component want to _add_
232+
a class than completely override the existing ones. For `class`, the order of
233+
`...attributes` will determine the order of merging. Putting it before:
234+
235+
```handlebars
236+
<p ...attributes class="friend-greeting">Hello {{@friend}}, I'm {{this.name}}!</p>
237+
```
238+
239+
Results in:
240+
241+
```html
242+
<p class="red-alert friend-greeting">Hello {{@friend}}, I'm {{this.name}}!</p>
243+
```
244+
245+
And putting it after:
246+
247+
```handlebars
248+
<p class="friend-greeting" ...attributes>Hello {{@friend}}, I'm {{this.name}}!</p>
249+
```
250+
251+
Results in:
252+
253+
```html
254+
<p class="friend-greeting red-alert">Hello {{@friend}}, I'm {{this.name}}!</p>
255+
```
256+
257+
#### Attributes and Modifiers
258+
259+
Modifiers are a concept that we haven't covered too deeply just yet, but they're
260+
similar to helpers, except that they are applied directly to elements:
261+
262+
```handlebars
263+
<div {{did-insert this.setupElement}}>
264+
...
265+
</div>
266+
```
267+
268+
Modifiers can also be applied to components, and when they are, they are also
269+
passed forward and applied to an element with `...attributes`:
270+
271+
```handlebars
272+
<Tooltip {{did-insert this.setupTooltip}}/>
273+
```

guides/release/components/block-params.md

Lines changed: 0 additions & 52 deletions
This file was deleted.

0 commit comments

Comments
 (0)