Bindings are how component state reaches the DOM; mixing up properties and attributes is a classic Angular bug.
Angular templates are HTML plus bindings that Angular keeps in sync with component state.
Interpolation {{ expression }} renders text. Expressions are a TypeScript-like subset: property access, method calls, operators, template literals, ?. and ??. They can't contain assignments, new or statements, and should be cheap and side-effect free because they are re-evaluated during change detection.
Property binding uses square brackets: [disabled]="isSaving()", [src]="avatarUrl()", [value]="name()". It sets a DOM property (or a component input) to the evaluated value, keeping its type: booleans stay booleans. Without brackets, disabled="false" sets the attribute to the string "false", which still disables the button.
Some things are attributes, not properties (ARIA, colspan, SVG attributes, data-*). Bind them with `[attr.name]`: [attr.aria-label]="label()". A null value removes the attribute.
Class and style bindings:
[class.active]="isActive()"toggles one class;[class]="{ active: a(), disabled: d() }"takes an object, array or string.[style.width.px]="width()"sets one style with a unit;[style]="{ color: c() }"takes an object.
Angular sanitises values bound to innerHTML, URLs and styles to prevent cross-site scripting. You can declare template-local variables with `@let`: @let total = price() * qty();.
Example
import { Component, signal, computed } from '@angular/core';
@Component({
selector: 'app-product',
template: `
@let total = price() * qty();
<h2 [class.sold-out]="stock() === 0" [title]="name()">{{ name() }}</h2>
<img [src]="imageUrl()" [attr.aria-label]="'Photo of ' + name()" />
<p>{{ qty() }} x {{ price() }} = {{ total }}</p>
<div class="bar" [style.width.%]="stockPercent()"></div>
<button [disabled]="stock() === 0">Buy</button> <!-- real boolean -->
`,
})
export class Product {
protected name = signal('Keyboard');
protected price = signal(49);
protected qty = signal(2);
protected stock = signal(3);
protected imageUrl = signal('/img/keyboard.png');
protected stockPercent = computed(() => (this.stock() / 10) * 100); // 30
}Remember
{{ }}renders text;[prop]="expr"binds DOM properties and inputs with real types- Use
[attr.x]for attributes such as ARIA and colspan [class.x],[style.prop.unit]and object forms handle classes and styles
Watch out for
- Writing
disabled="{{ isDisabled }}"ordisabled="false"; use[disabled]="isDisabled()".