Template bindings

Angular · Essentials · 11 min

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 }}" or disabled="false"; use [disabled]="isDisabled()".

Check yourself

  1. Which binding correctly sets an ARIA attribute from component state?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: angular.dev/guide/templates/binding