304 lines
12 KiB
Markdown
304 lines
12 KiB
Markdown
|
# Attribute, class, and style bindings
|
||
|
|
||
|
The template syntax provides specialized one-way bindings for scenarios less well-suited to property binding.
|
||
|
|
||
|
<div class="alert is-helpful">
|
||
|
|
||
|
See the <live-example></live-example> for a working example containing the code snippets in this guide.
|
||
|
|
||
|
</div>
|
||
|
|
||
|
|
||
|
## Attribute binding
|
||
|
|
||
|
Set the value of an attribute directly with an **attribute binding**. This is the only exception to the rule that a binding sets a target property and the only binding that creates and sets an attribute.
|
||
|
|
||
|
Usually, setting an element property with a [property binding](guide/property-binding)
|
||
|
is preferable to setting the attribute with a string. However, sometimes
|
||
|
there is no element property to bind, so attribute binding is the solution.
|
||
|
|
||
|
Consider the [ARIA](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA) and
|
||
|
[SVG](https://developer.mozilla.org/en-US/docs/Web/SVG). They are purely attributes, don't correspond to element properties, and don't set element properties. In these cases, there are no property targets to bind to.
|
||
|
|
||
|
Attribute binding syntax resembles property binding, but
|
||
|
instead of an element property between brackets, start with the prefix `attr`,
|
||
|
followed by a dot (`.`), and the name of the attribute.
|
||
|
You then set the attribute value, using an expression that resolves to a string,
|
||
|
or remove the attribute when the expression resolves to `null`.
|
||
|
|
||
|
One of the primary use cases for attribute binding
|
||
|
is to set ARIA attributes, as in this example:
|
||
|
|
||
|
<code-example path="attribute-binding/src/app/app.component.html" region="attrib-binding-aria" header="src/app/app.component.html"></code-example>
|
||
|
|
||
|
{@a colspan}
|
||
|
|
||
|
<div class="alert is-helpful">
|
||
|
|
||
|
#### `colspan` and `colSpan`
|
||
|
|
||
|
Notice the difference between the `colspan` attribute and the `colSpan` property.
|
||
|
|
||
|
If you wrote something like this:
|
||
|
|
||
|
<code-example language="html">
|
||
|
<tr><td colspan="{{1 + 1}}">Three-Four</td></tr>
|
||
|
</code-example>
|
||
|
|
||
|
You'd get this error:
|
||
|
|
||
|
<code-example language="bash">
|
||
|
Template parse errors:
|
||
|
Can't bind to 'colspan' since it isn't a known native property
|
||
|
</code-example>
|
||
|
|
||
|
As the message says, the `<td>` element does not have a `colspan` property. This is true
|
||
|
because `colspan` is an attribute—`colSpan`, with a capital `S`, is the
|
||
|
corresponding property. Interpolation and property binding can set only *properties*, not attributes.
|
||
|
|
||
|
Instead, you'd use property binding and write it like this:
|
||
|
|
||
|
<code-example path="attribute-binding/src/app/app.component.html" region="colSpan" header="src/app/app.component.html"></code-example>
|
||
|
|
||
|
</div>
|
||
|
|
||
|
<hr/>
|
||
|
|
||
|
{@a class-binding}
|
||
|
|
||
|
## Class binding
|
||
|
|
||
|
Here's how to set the `class` attribute without a binding in plain HTML:
|
||
|
|
||
|
```html
|
||
|
<!-- standard class attribute setting -->
|
||
|
<div class="foo bar">Some text</div>
|
||
|
```
|
||
|
|
||
|
You can also add and remove CSS class names from an element's `class` attribute with a **class binding**.
|
||
|
|
||
|
To create a single class binding, start with the prefix `class` followed by a dot (`.`) and the name of the CSS class (for example, `[class.foo]="hasFoo"`).
|
||
|
Angular adds the class when the bound expression is truthy, and it removes the class when the expression is falsy (with the exception of `undefined`, see [styling delegation](#styling-delegation)).
|
||
|
|
||
|
To create a binding to multiple classes, use a generic `[class]` binding without the dot (for example, `[class]="classExpr"`).
|
||
|
The expression can be a space-delimited string of class names, or you can format it as an object with class names as the keys and truthy/falsy expressions as the values.
|
||
|
With object format, Angular will add a class only if its associated value is truthy.
|
||
|
|
||
|
It's important to note that with any object-like expression (`object`, `Array`, `Map`, `Set`, etc), the identity of the object must change for the class list to be updated.
|
||
|
Updating the property without changing object identity will have no effect.
|
||
|
|
||
|
If there are multiple bindings to the same class name, conflicts are resolved using [styling precedence](#styling-precedence).
|
||
|
|
||
|
<style>
|
||
|
td, th {vertical-align: top}
|
||
|
</style>
|
||
|
|
||
|
<table width="100%">
|
||
|
<col width="15%">
|
||
|
</col>
|
||
|
<col width="20%">
|
||
|
</col>
|
||
|
<col width="35%">
|
||
|
</col>
|
||
|
<col width="30%">
|
||
|
</col>
|
||
|
<tr>
|
||
|
<th>
|
||
|
Binding Type
|
||
|
</th>
|
||
|
<th>
|
||
|
Syntax
|
||
|
</th>
|
||
|
<th>
|
||
|
Input Type
|
||
|
</th>
|
||
|
<th>
|
||
|
Example Input Values
|
||
|
</th>
|
||
|
</tr>
|
||
|
<tr>
|
||
|
<td>Single class binding</td>
|
||
|
<td><code>[class.foo]="hasFoo"</code></td>
|
||
|
<td><code>boolean | undefined | null</code></td>
|
||
|
<td><code>true</code>, <code>false</code></td>
|
||
|
</tr>
|
||
|
<tr>
|
||
|
<td rowspan=3>Multi-class binding</td>
|
||
|
<td rowspan=3><code>[class]="classExpr"</code></td>
|
||
|
<td><code>string</code></td>
|
||
|
<td><code>"my-class-1 my-class-2 my-class-3"</code></td>
|
||
|
</tr>
|
||
|
<tr>
|
||
|
<td><code>{[key: string]: boolean | undefined | null}</code></td>
|
||
|
<td><code>{foo: true, bar: false}</code></td>
|
||
|
</tr>
|
||
|
<tr>
|
||
|
<td><code>Array</code><<code>string</code>></td>
|
||
|
<td><code>['foo', 'bar']</code></td>
|
||
|
</tr>
|
||
|
</table>
|
||
|
|
||
|
|
||
|
The [NgClass](guide/built-in-directives/#ngclass) directive can be used as an alternative to direct `[class]` bindings.
|
||
|
However, using the above class binding syntax without `NgClass` is preferred because due to improvements in class binding in Angular, `NgClass` no longer provides significant value, and might eventually be removed in the future.
|
||
|
|
||
|
|
||
|
<hr/>
|
||
|
|
||
|
## Style binding
|
||
|
|
||
|
Here's how to set the `style` attribute without a binding in plain HTML:
|
||
|
|
||
|
```html
|
||
|
<!-- standard style attribute setting -->
|
||
|
<div style="color: blue">Some text</div>
|
||
|
```
|
||
|
|
||
|
You can also set styles dynamically with a **style binding**.
|
||
|
|
||
|
To create a single style binding, start with the prefix `style` followed by a dot (`.`) and the name of the CSS style property (for example, `[style.width]="width"`).
|
||
|
The property will be set to the value of the bound expression, which is normally a string.
|
||
|
Optionally, you can add a unit extension like `em` or `%`, which requires a number type.
|
||
|
|
||
|
<div class="alert is-helpful">
|
||
|
|
||
|
Note that a _style property_ name can be written in either
|
||
|
[dash-case](guide/glossary#dash-case), as shown above, or
|
||
|
[camelCase](guide/glossary#camelcase), such as `fontSize`.
|
||
|
|
||
|
</div>
|
||
|
|
||
|
If there are multiple styles you'd like to toggle, you can bind to the `[style]` property directly without the dot (for example, `[style]="styleExpr"`).
|
||
|
The expression attached to the `[style]` binding is most often a string list of styles like `"width: 100px; height: 100px;"`.
|
||
|
|
||
|
You can also format the expression as an object with style names as the keys and style values as the values, like `{width: '100px', height: '100px'}`.
|
||
|
It's important to note that with any object-like expression (`object`, `Array`, `Map`, `Set`, etc), the identity of the object must change for the class list to be updated.
|
||
|
Updating the property without changing object identity will have no effect.
|
||
|
|
||
|
If there are multiple bindings to the same style property, conflicts are resolved using [styling precedence rules](#styling-precedence).
|
||
|
|
||
|
<style>
|
||
|
td, th {vertical-align: top}
|
||
|
</style>
|
||
|
|
||
|
<table width="100%">
|
||
|
<col width="15%">
|
||
|
</col>
|
||
|
<col width="20%">
|
||
|
</col>
|
||
|
<col width="35%">
|
||
|
</col>
|
||
|
<col width="30%">
|
||
|
</col>
|
||
|
<tr>
|
||
|
<th>
|
||
|
Binding Type
|
||
|
</th>
|
||
|
<th>
|
||
|
Syntax
|
||
|
</th>
|
||
|
<th>
|
||
|
Input Type
|
||
|
</th>
|
||
|
<th>
|
||
|
Example Input Values
|
||
|
</th>
|
||
|
</tr>
|
||
|
<tr>
|
||
|
<td>Single style binding</td>
|
||
|
<td><code>[style.width]="width"</code></td>
|
||
|
<td><code>string | undefined | null</code></td>
|
||
|
<td><code>"100px"</code></td>
|
||
|
</tr>
|
||
|
<tr>
|
||
|
<tr>
|
||
|
<td>Single style binding with units</td>
|
||
|
<td><code>[style.width.px]="width"</code></td>
|
||
|
<td><code>number | undefined | null</code></td>
|
||
|
<td><code>100</code></td>
|
||
|
</tr>
|
||
|
<tr>
|
||
|
<td rowspan=3>Multi-style binding</td>
|
||
|
<td rowspan=3><code>[style]="styleExpr"</code></td>
|
||
|
<td><code>string</code></td>
|
||
|
<td><code>"width: 100px; height: 100px"</code></td>
|
||
|
</tr>
|
||
|
<tr>
|
||
|
<td><code>{[key: string]: string | undefined | null}</code></td>
|
||
|
<td><code>{width: '100px', height: '100px'}</code></td>
|
||
|
</tr>
|
||
|
<tr>
|
||
|
<td><code>Array</code><<code>string</code>></td>
|
||
|
<td><code>['width', '100px']</code></td>
|
||
|
</tr>
|
||
|
</table>
|
||
|
|
||
|
The [NgStyle](guide/built-in-directives/#ngstyle) directive can be used as an alternative to direct `[style]` bindings.
|
||
|
However, using the above style binding syntax without `NgStyle` is preferred because due to improvements in style binding in Angular, `NgStyle` no longer provides significant value, and might eventually be removed in the future.
|
||
|
|
||
|
|
||
|
<hr/>
|
||
|
|
||
|
{@a styling-precedence}
|
||
|
|
||
|
## Styling Precedence
|
||
|
|
||
|
A single HTML element can have its CSS class list and style values bound to multiple sources (for example, host bindings from multiple directives).
|
||
|
|
||
|
When there are multiple bindings to the same class name or style property, Angular uses a set of precedence rules to resolve conflicts and determine which classes or styles are ultimately applied to the element.
|
||
|
|
||
|
<div class="alert is-helpful">
|
||
|
<h4>Styling precedence (highest to lowest)</h4>
|
||
|
|
||
|
1. Template bindings
|
||
|
1. Property binding (for example, `<div [class.foo]="hasFoo">` or `<div [style.color]="color">`)
|
||
|
1. Map binding (for example, `<div [class]="classExpr">` or `<div [style]="styleExpr">`)
|
||
|
1. Static value (for example, `<div class="foo">` or `<div style="color: blue">`)
|
||
|
1. Directive host bindings
|
||
|
1. Property binding (for example, `host: {'[class.foo]': 'hasFoo'}` or `host: {'[style.color]': 'color'}`)
|
||
|
1. Map binding (for example, `host: {'[class]': 'classExpr'}` or `host: {'[style]': 'styleExpr'}`)
|
||
|
1. Static value (for example, `host: {'class': 'foo'}` or `host: {'style': 'color: blue'}`)
|
||
|
1. Component host bindings
|
||
|
1. Property binding (for example, `host: {'[class.foo]': 'hasFoo'}` or `host: {'[style.color]': 'color'}`)
|
||
|
1. Map binding (for example, `host: {'[class]': 'classExpr'}` or `host: {'[style]': 'styleExpr'}`)
|
||
|
1. Static value (for example, `host: {'class': 'foo'}` or `host: {'style': 'color: blue'}`)
|
||
|
|
||
|
</div>
|
||
|
|
||
|
The more specific a class or style binding is, the higher its precedence.
|
||
|
|
||
|
A binding to a specific class (for example, `[class.foo]`) will take precedence over a generic `[class]` binding, and a binding to a specific style (for example, `[style.bar]`) will take precedence over a generic `[style]` binding.
|
||
|
|
||
|
<code-example path="attribute-binding/src/app/app.component.html" region="basic-specificity" header="src/app/app.component.html"></code-example>
|
||
|
|
||
|
Specificity rules also apply when it comes to bindings that originate from different sources.
|
||
|
It's possible for an element to have bindings in the template where it's declared, from host bindings on matched directives, and from host bindings on matched components.
|
||
|
|
||
|
Template bindings are the most specific because they apply to the element directly and exclusively, so they have the highest precedence.
|
||
|
|
||
|
Directive host bindings are considered less specific because directives can be used in multiple locations, so they have a lower precedence than template bindings.
|
||
|
|
||
|
Directives often augment component behavior, so host bindings from components have the lowest precedence.
|
||
|
|
||
|
<code-example path="attribute-binding/src/app/app.component.html" region="source-specificity" header="src/app/app.component.html"></code-example>
|
||
|
|
||
|
In addition, bindings take precedence over static attributes.
|
||
|
|
||
|
In the following case, `class` and `[class]` have similar specificity, but the `[class]` binding will take precedence because it is dynamic.
|
||
|
|
||
|
<code-example path="attribute-binding/src/app/app.component.html" region="dynamic-priority" header="src/app/app.component.html"></code-example>
|
||
|
|
||
|
{@a styling-delegation}
|
||
|
### Delegating to styles with lower precedence
|
||
|
|
||
|
It is possible for higher precedence styles to "delegate" to lower precedence styles using `undefined` values.
|
||
|
Whereas setting a style property to `null` ensures the style is removed, setting it to `undefined` will cause Angular to fall back to the next-highest precedence binding to that style.
|
||
|
|
||
|
For example, consider the following template:
|
||
|
|
||
|
<code-example path="attribute-binding/src/app/app.component.html" region="style-delegation" header="src/app/app.component.html"></code-example>
|
||
|
|
||
|
Imagine that the `dirWithHostBinding` directive and the `comp-with-host-binding` component both have a `[style.width]` host binding.
|
||
|
In that case, if `dirWithHostBinding` sets its binding to `undefined`, the `width` property will fall back to the value of the `comp-with-host-binding` host binding.
|
||
|
However, if `dirWithHostBinding` sets its binding to `null`, the `width` property will be removed entirely.
|