Correct way to pass arguments to built-in components with angle-bracket syntax

Hi everyone,

I’m updating the Rock & Roll with Ember.js book to 3.10 and started converting the built-in components to the angle-bracket syntax (see release blog post).

Both the release blog post and the original RFC gives examples where the HTML attributes passed to these components are prefixed with @:

<Input
  @type="text"
  @class="rr-input"
  @placeholder="New band"
  @value={{newBandName}}
/>

This just doesn’t feel right to me, let me explain why.

One of the advantages of angle-bracket invocation is that readers of the template can easily tell apart at a glance HTML attributes from component arguments. One such example would be:

<StarRating
  class="fr"
  @rating={{song.rating}}
  @onClick={{action "updateRating" song}}
/>

I can see right away that class is an HTML attribute (and will be bound to the outer element in classic components) @rating and @onClick are component arguments.

Prefixing all keys of a component call with @ blurs that line and is also very hard to teach. Why should one use class for custom (non built-in) components and @class in built-in ones? Why make an exception with what could be regarded as teaching examples?

I went ahead and tried using non-prefixed keys for Input, Textarea and LinkTo and it works for all attributes, except value:

<Input
  type="text"
  class="rr-input"
  placeholder="New band"
  @value={{newBandName}}
/>

<LinkTo
  @route="bands.band"
  @model={{band.id}}
  class="rr-band-link"
  data-test-rr="band-link"
>
  {{capitalize band.name}}
  <span class="rr-pointer">
    {{fa-icon "angle-right"}}
  </span>
</LinkTo>

<Textarea
  class="rr-textarea"
  data-test-rr="band-description-field"
  @value={{@band.description}}
  @focus-out={{action (mut showErrors.description) true}}
/>

There is probably a reason I’m missing but if we have to use @class, @type, @disabled, @placeholder, I’d like to understand it.

1 Like

The RFC specifically mentions this:

As mentioned above, this will allow Ember developers to invoke components with a consistent syntax, which should make it easier to teach.

This RFC does not aim to “fix” issues or quirks with the existing APIs – it merely attempts to provide a way to do the equivalent invocation in angle bracket syntax.

Specifically, because we have existing Ember.Component implementations that folks are currently relying on (e.g. for extending) that accept these things as arguments (since there was no other way prior to angle invocation). This is why the argument forms are “still around”. For all of the arguments that are only used internally for attributeBindings, I personally would absolutely pass them as attributes (the non-@ prefixed versions) instead of arguments.

As the guides are updated for the newer angle bracket invocation I’d expect the attribute passing to be used wherever possible.

1 Like

For example, in ember-learn/guides-source#797 you’ll notice passing some attributes. Though most of the examples of {{input}} in the guides didn’t use the arguments that were just used for attributeBindings.

1 Like

I understood this as providing a consistent syntax, namely angle-bracket invocation, to call all components whether built-in or not. Not how keys in the component call are prefixed.

Ah, I have now found this comment in the source code of the Input component (link) (sorry, should have read the source first :blush:)

   ### `<input>` HTML Attributes to Avoid
    In most cases, if you want to pass an attribute to the underlying HTML `<input>` element, you
    can pass the attribute directly, just like any other Ember component.
    (...)
    However, there are a few attributes where you **must** use the `@` version.
    * `@type`: This argument is used to control which Ember component is used under the hood
    * `@value`: The `@value` argument installs a two-way binding onto the element. If you wanted a
      one-way binding, use `<input>` with the `value` property and the `input` event instead.
    * `@checked` (for checkboxes): like `@value`, the `@checked` argument installs a two-way binding
      onto the element. If you wanted a one-way binding, use `<input type="checkbox">` with
      `checked` and the `input` event instead.

The “non-special” attributes that just work (class, placeholder, disabled, cols, rows, etc.) are not explicitly included in attributeBindings or at least I haven’t found them. I assume they work in some other way.

Anyway, thanks a ton, things are definitely clearer now!

2 Likes