Data Binding in Angular
Data binding is the mechanism that keeps your component's TypeScript class and its HTML template in sync. Angular provides four distinct binding types, each serving a different direction of data flow. Mastering all four lets you build reactive, interactive UIs without manual DOM manipulation.
The Four Binding Types
Type | Syntax | Direction | Use Case |
|---|---|---|---|
Interpolation | {{ value }} | Class → Template | Display text in the DOM |
Property Binding | [property]="expr" | Class → Template | Set DOM/component properties |
Event Binding | (event)="handler()" | Template → Class | React to user events |
Two-Way Binding | [(ngModel)]="prop" | Both directions | Form inputs, live sync |
Interpolation — {{}}
Interpolation evaluates a template expression and converts the result to a string that is inserted into the HTML.
@Component({
selector: 'app-greeting',
standalone: true,
template: `
<h1>Hello, {{ name }}!</h1>
<p>Today is {{ today | date:'longDate' }}</p>
<p>2 + 2 = {{ 2 + 2 }}</p>
`,
})
export class GreetingComponent {
name = 'Angular';
today = new Date();
}
textContent property. The double-curly syntax and [textContent]="expr" are equivalent for string values.Property Binding — [property]
Property binding sets a DOM element's property or a child component's @Input to the value of a TypeScript expression. The square brackets signal Angular to evaluate the right-hand side as an expression rather than a plain string.
@Component({
selector: 'app-demo',
standalone: true,
template: `
<!-- DOM property binding -->
<img [src]="imageUrl" [alt]="imageAlt" />
<button [disabled]="isLoading">Submit</button>
<!-- Component @Input binding -->
<app-progress [value]="completionPercent" [max]="100" />
<!-- Class and style bindings (shorthand property bindings) -->
<div [class.active]="isActive" [style.color]="textColor">
Dynamic styling
</div>
`,
})
export class DemoComponent {
imageUrl = 'https://example.com/logo.png';
imageAlt = 'Company logo';
isLoading = false;
completionPercent = 42;
isActive = true;
textColor = '#007bff';
}
Event Binding — (event)
Event binding listens for DOM events on elements and calls a method in your component class when they occur. The parentheses syntax mirrors the direction: data flows from the template to the class.
@Component({
selector: 'app-counter',
standalone: true,
template: `
<p>Count: {{ count }}</p>
<button (click)="increment()">+</button>
<button (click)="decrement()">-</button>
<input (keyup.enter)="onEnter($event)" placeholder="Press Enter" />
`,
})
export class CounterComponent {
count = 0;
increment() { this.count++; }
decrement() { this.count--; }
onEnter(event: KeyboardEvent) {
const input = event.target as HTMLInputElement;
console.log('Enter pressed, value:', input.value);
}
}
Two-Way Binding — [(ngModel)]
Two-way binding combines property binding and event binding into a single syntax: the "banana in a box" [(ngModel)]. The property binding pushes the value into the input; the event binding pushes changes back into the component property — both happen automatically.
import { Component } from '@angular/core';
import { FormsModule } from '@angular/forms';
@Component({
selector: 'app-name-form',
standalone: true,
imports: [FormsModule],
template: `
<input [(ngModel)]="username" placeholder="Enter username" />
<p>You typed: {{ username }}</p>
`,
})
export class NameFormComponent {
username = '';
}
Attribute, Class, and Style Bindings
Beyond standard property binding, Angular provides dedicated syntax for attributes, CSS classes, and inline styles.
@Component({
selector: 'app-badge',
standalone: true,
template: `
<!-- Attribute binding (for HTML attributes without a DOM property counterpart) -->
<td [attr.colspan]="columnSpan">Merged cell</td>
<button [attr.aria-label]="buttonLabel">Icon</button>
<!-- Class binding — adds/removes a single class -->
<div [class.highlight]="isHighlighted">Text</div>
<!-- NgClass — add/remove multiple classes at once -->
<div [ngClass]="{ 'active': isActive, 'error': hasError }">Status</div>
<!-- Style binding — set one inline style -->
<p [style.font-size.px]="fontSize">Resizable</p>
<!-- NgStyle — set multiple styles at once -->
<p [ngStyle]="{ 'color': textColor, 'font-weight': isBold ? 'bold' : 'normal' }">
Styled text
</p>
`,
})
export class BadgeComponent {
columnSpan = 3;
buttonLabel = 'Close dialog';
isHighlighted = true;
isActive = false;
hasError = true;
fontSize = 18;
textColor = 'darkblue';
isBold = true;
}
Template Reference Variables
A template reference variable (declared with #name) gives you a reference to a DOM element or component instance right inside the template — no @ViewChild needed for simple cases.
@Component({
selector: 'app-input-demo',
standalone: true,
template: `
<input #nameInput placeholder="Type something" />
<button (click)="greet(nameInput.value)">Greet</button>
<p>{{ message }}</p>
`,
})
export class InputDemoComponent {
message = '';
greet(name: string) {
this.message = `Hello, ${name}!`;
}
}
Safe Navigation Operator (?.) in Templates
When binding to an object that might be null or undefined, use Angular's safe navigation operator to avoid runtime errors.
<!-- Without safe navigation: crashes if user is null -->
<p>{{ user.address.city }}</p>
<!-- With safe navigation: returns undefined silently -->
<p>{{ user?.address?.city }}</p>
<!-- Combined with nullish coalescing -->
<p>{{ user?.name ?? 'Guest' }}</p>
Binding Summary
Interpolation {{ }} — convert an expression to text in the template.
Property binding [prop] — push data from class to template/child component.
Event binding (event) — respond to DOM or component events in the class.
Two-way binding [(ngModel)] — sync input value and class property simultaneously.
Attribute binding [attr.x] — set HTML attributes that have no DOM property.
Class binding [class.x] / [ngClass] — toggle CSS classes dynamically.
Style binding [style.x] / [ngStyle] — set inline styles dynamically.
Template reference #var — grab a DOM/component reference in the template.