Angular · Components

Property Binding

Property binding sets a property of an element or component from your data: enable a button, set an image, pass data to a child, toggle a class. Square brackets mean “this value comes from the class”.

Beginner 5 min Lesson 9 of 38Examples target Angular 20 with standalone components. Each lesson states the version it was verified against.
Before you start:Data Binding
0/38 in Angular
On this page · 5 min · Beginner

Learning objectives#

  • Bind DOM properties like disabled, src and value
  • Pass data to child component inputs
  • Bind attributes, classes and styles
  • Know the difference between properties and attributes

Explanation#

[property]="expression" sets a DOM property: [disabled]="saving()", [src]="photoUrl", [value]="query".

The same syntax passes data to a child component’s input: <app-product-card [price]="p.price" />.

Some HTML attributes have no matching DOM property (aria-*, colspan, data-*). Use [attr.aria-label]="label".

Toggle one class with [class.active]="isActive()"; set several with [class]="{ active: isActive(), error: hasError() }". Styles: [style.width.px]="width" or [style.color]="color".

Without brackets, the attribute value is a plain string: price="20" passes the text "20", while [price]="20" passes the number 20.

Syntax#

HTML Illustrative
<button [disabled]="saving()">Save</button>
<img [src]="photoUrl" [alt]="name" />
<app-rating [stars]="4" />
<td [attr.colspan]="span">…</td>
<li [class.active]="selected()" [style.font-weight]="bold ? 'bold' : 'normal'">…</li>

Code example: A progress card driven by property bindings#

TypeScriptupload-card.ts Illustrative
import { Component, computed, signal } from '@angular/core';


@Component({
  selector: 'app-upload-card',
  template: `
    <article [class.done]="isDone()" [class.error]="failed()">
      <img [src]="iconUrl()" [alt]="fileName + ' icon'" width="32" height="32" />
      <strong>{{ fileName }}</strong>


      <div class="bar" [attr.aria-valuenow]="progress()" role="progressbar" aria-valuemin="0" aria-valuemax="100">
        <div class="fill" [style.width.%]="progress()"></div>
      </div>


      <button [disabled]="isDone() || failed()" (click)="advance()">Upload 25%</button>
      <button [hidden]="!failed()" (click)="retry()">Retry</button>
    </article>
  `,
  styles: `
    .bar { background: #eee; height: 8px; width: 200px; }
    .fill { background: #4f46e5; height: 8px; }
    .done { border-left: 4px solid green; padding-left: 8px; }
    .error { border-left: 4px solid red; padding-left: 8px; }
  `,
})
export class UploadCard {
  fileName = 'invoice.pdf';
  progress = signal(0);
  failed = signal(false);
  isDone = computed(() => this.progress() >= 100);
  iconUrl = computed(() => (this.isDone() ? 'https://img.icons8.com/color/32/checked.png' : 'https://img.icons8.com/color/32/pdf.png'));


  advance() {
    if (this.progress() === 50 && !this.failed()) { this.failed.set(true); return; }   // simulate one failure
    this.progress.update(p => Math.min(100, p + 25));
  }


  retry() {
    this.failed.set(false);
    this.progress.update(p => p + 25);
  }
}
Expected output
Each “Upload 25%” click widens the purple bar. At 50% the card gets a red border, the Upload button is disabled and Retry appears. Retry continues; at 100% the border turns green, the icon changes to a tick and the Upload button is disabled. Screen readers get the current value through aria-valuenow.

This example is illustrative. Copy or download it and run it in your own environment; the expected output is shown above.

Step-by-step explanation#

  1. [class.done] and [class.error] toggle single classes from signals.
  2. [src] swaps the icon when the computed isDone() changes.
  3. [attr.aria-valuenow] sets an attribute that has no DOM property.
  4. [style.width.%] sets the width with a unit suffix.
  5. [disabled] and [hidden] enable, disable and hide buttons from state.

Real-world use case#

Data tables bind [class.selected] on rows, [disabled] on bulk-action buttons, and [attr.aria-sort] on column headers for accessible sorting.

Common mistakes#

Binding aria-* as properties

[aria-label]="x" fails because there is no ariaLabel binding target in some cases; use [attr.aria-label].

Forgetting brackets for numbers or booleans

[disabled]="false" is false; disabled="false" is the string "false" — and the button is disabled!

Large style objects in templates

Many [style] bindings make templates noisy. Prefer classes.

Best practices#

  • Brackets for anything that is not a fixed string
  • [attr.] for aria and data attributes
  • [class.x] for toggles, CSS for the look
  • Bind to computed signals for derived values

Remember this#

[prop]="expr" = set from the class.

No brackets = plain string.

[attr.x], [class.x], [style.x.unit] for special cases.

Practice exercise#

Your task: Show a list of menu items where the current one has the class "active" and aria-current="page".

Need a hint?
[class.active]="item === current()" and [attr.aria-current]="item === current() ? 'page' : null".
Show solution
TypeScript Illustrative
import { Component, signal } from '@angular/core';


@Component({
  selector: 'app-menu',
  template: `
    <nav>
      @for (item of items; track item) {
        <a href="#" [class.active]="item === current()" [attr.aria-current]="item === current() ? 'page' : null"
           (click)="current.set(item); $event.preventDefault()">{{ item }}</a>
      }
    </nav>
  `,
  styles: `.active { font-weight: bold; }`,
})
export class Menu {
  items = ['Home', 'Shop', 'Contact'];
  current = signal('Home');
}

Mini quiz#

Property Binding quizMultiple choice Question 1 of 3

Which binding sets an ARIA attribute?

Score so far: 0/0

Want more? Practise all Angular MCQs · Angular interview questions

My notes#

Saved in this browser.

Progress is saved in this browser. Sign in to sync.