Forwarding Element Modifiers with ...attributes (3.11)
Angle bracket components receive some clarity about how they handle forwarding element modifiers.
Summary
Angle brackets have had the ...attributes
feature (known as "splattributes") since 3.4, but since Element Modifiers were introduced, their relationship with splattributes was never formalized.
This feature clarifies that relationship.
First, a quick recap of ...attributes
:
{{! application.hbs }}
<MyButton aria-label='Home'>
<Icon 'home' />
</MyButton>
{{! my-button.hbs }}
<span class='my-button-wrapper'>
<button ...attributes>
{{yield}}
</button>
</span>
Here, MyButton
's template is using ...attriubutes
to forward any attributes from the parent calling context down to the <button>
HTML element in its template. You can see how ...attributes
gives component authors the power to choose which element to forward attributes to – in this case, the author wanted attributes like aria-label
to be applied to the underlying button
element (as opposed to the outer span
wrapper).
The final HTML is
<span class='my-button-wrapper'>
<button aria-label='Home'>
<i class='fa-home' />
</button>
</span>
The Forwarding Element Modifiers with ...attributes feature clarifies that the ...attributes
syntax also forwards element modifiers, in addition to HTML attributes like aria-label
.
This means that if the calling context in the application template were to add an {{action}}
modifier:
{{! application.hbs }}
<MyButton {{action 'handleClick'}} aria-label='Home'>
<Icon 'home' />
</MyButton>
then this click handler would be forwarded down to the root HTML <button>
element. So, element modifiers (like the built-in {{action}}
modifier, as well as any custom modifiers you write on your own) are forwarded alongside HTML attributes.
In addition, this feature also ensures that modifiers can be also be forwarded across chains of components using the same ...attributes
syntax.
That means if we were to make a new Ember Component called FancyButton
that itself renders the Ember Component MyButton
{{! application.hbs }}
<FancyButton>
Hello!
</FancyButton>
{{! fancy-button.hbs }}
<MyButton class='background-blue'>
{{yield}}
</MyButton>
{{! my-button.hbs }}
<span class='my-button-wrapper'>
<button ...attributes>
{{yield}}
</button>
</span>
and the calling context added a modifier
{{! application.hbs }}
- <FancyButton>
+ <FancyButton {{action 'handleClick'}}>
Hello!
</FancyButton>
we could author FancyButton
to forward all of its modifiers down to MyButton
(which in turn forwards them to the underlying <button>
HTML element):
{{! fancy-button.hbs }}
- <MyButton class='background-blue'>
+ <MyButton class='background-blue' ...attributes>
{{yield}}
</MyButton>
As you can see, this enables more powerful composition, since the middle component layers don't need to be aware of all possible attributes and modifiers that might be used at the top-level calling context.