:attribute
Inside component templates, bind attributes to reactive expressions by prefixing the attribute name with a colon.
Syntax
<element :attribute="expresssion"></element>
<element :class+name="condition"></element>
Parameters
- :attribute
- The attribute name to bind the expression to, prefixed with a colon.
- expression
- An expression evaluating to a value, usually one with live dependencies. The expression is assigned to the attribute as-is, which implicitly causes it to be converted to a string if it is not already one. There is one exception; when the expression evaluates to null or undefined, the attribute is removed.
- :class+name
- A special case for handling individual classes. The class is included if the expression evaluates to something truthy. Note that, because attributes are cases-insensitive, it is not possible to set class names with uppercase characters using this form. Additionally, it is not advised to mix a dynamic :class attribute with individual :class+name expressions, since the :class attribute would overwrite any individually toggled classes whenever it updates. It is however perfectly fine to use a class attribute with :class+name expressions, since the class never updates (and therefore never overwrites dynamic classes).
- condition
- An expression determining whether to toggle the class name on or off.
Note: In many cases, the .property
syntax may be more appropriate, since properties don't need to do the string conversion that attributes need to. Specifically, boolean attributes don't work well with the :attribute syntax, needing :bool="$.condition ? '' : null" compared to the simpler .bool="$.condition".
Details
Like the in-template .property
syntax, assigning attributes using this syntax is equivalent to running an effect only when connected. When the custom element is disconnected from the DOM, attributes bound using this in-template syntax are not updated to avoid unnecessary work. When inserting the element back into the document, the effects are set up again, causing them to be set to their proper values. If the attributes need to be kept up-to-date even while the component is not connected, set them manually using an effect()
.
Examples
Custom links
Let's build a simple link component, similar to the native <a> element. We'll define an attribute to, that is optional, and defaults to #; that way, we can simply write <custom-link> without having to worry about <a> elements being invalid (since they require an href attribute). We'll also make the links open in a new tab, if the provided URL is not relative.
<title>custom-link</title>
<meta attribute="to" type="string" default="#">
<template mode="closed" delegates-focus="true">
<a :href="$.to" :target="$.target">
<slot></slot>
</a>
</template>
<script>
live.link($.$target, () => {
if($.to.startsWith('.') || $.to.startsWith('/')){
return null;
}
return '_blank';
});
</script>
We've set up a live.link()
to bind $.target to the value we pass to the :target attribute. When the URL given starts with a . or /, then $.target computes to null, causing the target attribute to be omitted entirely. As an alternative, we can use '_self' instead of null, since that's the default value for the target attribute on <a> elements.
Dynamic classes
Let's build on the previous example, and add a dynamic is-external class when the target is "_blank". We do this using a :class+is-external expression:
<title>custom-link</title>
<meta attribute="to" type="string" default="#">
<template mode="closed" delegates-focus="true">
<a
:href="$.to"
:target="$.isExternal ? '_blank' : null"
:class+is-external="$.isExternal"
>
<slot></slot>
</a>
</template>
<script>
live.link($.$isExternal, () => {
return !$.to.startsWith('.') && !$.to.startsWith('/');
});
</script>
Note that we could just as well use a :class expression here, but that would both be more verbose and less flexible; if we then want to add another class, we'll get either an unnecessarily complex :class attribute, or we need to convert them to individual :class+ expressions anyway.
Usage notes
While it is possible to pass both a non-reactive attribute and a reactive attribute at the same time (like <element :attribute="$.value" attribute="value">), the reactive attribute overwrites any static value already when the component connects. In general, this is advised against, since its behavior might not be obvious. Similarly, it is not advised to combine the :attribute syntax with the .property syntax if they are bound to the same thing; in this case, both will be competing to update the value whenever their live dependencies change, in an unspecified (and therefore unreliable) order.
See also