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.

Questions?

us, or ask in #media on Discord