Angular data binding: interpolation, property/attribute/class/style, events, and two-way ([(...)])

HighIntermediateAngular
Quick Answer

Angular data binding is mostly explicit one-way flow in one direction at a time: component state into the DOM, events back into the component, and two-way syntax only as sugar. The useful angle is when property versus attribute binding, or too much two-way binding, creates real production bugs.

Answer

Production mental model

Angular binding is not “everything is two-way.” Most bindings are one-way in one direction at a time: component state flows into the DOM, events flow back into the component, and [(...)] is just sugar on top. That distinction matters when you debug stale UI, misuse [attr.*] versus real DOM properties, or overuse two-way binding in complex forms.

Binding kind

Direction

What it binds to

Syntax

Example

Interpolation

Component ➜ View

Text nodes / attribute text

{{ expr }}

Hello {{ user.name }}

Property binding

Component ➜ View

DOM properties (not HTML attributes)

[prop]="expr"

<button [disabled]="loading">Save</button>

Attribute binding

Component ➜ View

HTML attributes (incl. ARIA, non-property attrs)

[attr.name]="expr"

<div [attr.aria-label]="label"></div>

Class binding

Component ➜ View

CSS classes

[class.foo]="expr"

<li [class.active]="isActive">...</li>

Style binding

Component ➜ View

Inline styles (+ units)

[style.width.px]="n"

<div [style.opacity]="alpha"></div>

Event binding

View ➜ Component

DOM events or child @Output

(event)="stmt"

<input (input)="onInput($event)">

Two-way binding

Both

Property + event (banana-in-a-box)

[(x)]="expr"

<input [(ngModel)]="name">

Angular binding types you’re expected to know in interviews (including attr/class/style prefixes).

Property vs Attribute (common interview trap)

[disabled] sets the DOM property (actual runtime behavior). [attr.disabled] sets/removes the HTML attribute string. For ARIA and many “string-only” attributes, you typically use [attr.*]. For real element state, use property binding.

HTML
<h2>{{ title }}</h2>

<img [src]="avatarUrl" [alt]="userName" />

<button type="button" [disabled]="isSaving" (click)="save()">
  Save
</button>

<input
  [value]="query"
  (input)="query = ($event.target as HTMLInputElement).value"
  [attr.aria-label]="'Search'"
/>

<div [class.error]="hasError" [style.width.px]="panelWidth"></div>
                  

Two-way binding is just sugar

[(x)] expands to [x] + (xChange) for custom components. In forms, [(ngModel)] is provided by template-driven forms (requires importing FormsModule).

TYPESCRIPT
import { Component, EventEmitter, Input, Output } from '@angular/core';

@Component({
  selector: 'app-rating',
  template: `
    <button type="button" (click)="set(1)">1</button>
    <button type="button" (click)="set(2)">2</button>
    <button type="button" (click)="set(3)">3</button>
    <span>Current: {{ value }}</span>
  `
})
export class RatingComponent {
  @Input() value = 0;
  @Output() valueChange = new EventEmitter<number>();

  set(v: number) {
    this.valueChange.emit(v);
  }
}

// parent template usage:
// <app-rating [(value)]="rating"></app-rating>
// expands to:
// <app-rating [value]="rating" (valueChange)="rating = $event"></app-rating>
                  

What makes the UI update?

Interview-safe answer

User events

Template events run handlers, state changes, then Angular checks affected views.

Async work (HTTP/timers/observables/promises)

Angular typically schedules change detection (Zone-based apps) or you trigger it explicitly in zoneless setups.

Parent input changes

When parent re-renders and updates an input binding, child updates in the next check.

Data binding + change detection are inseparable: bindings update during change detection passes.

Common pitfall

What goes wrong

Fix

Heavy expressions in templates

They run often during change detection and can cause jank

Move work to TS and bind to a field/signal/observable result

No FormsModule but using [(ngModel)]

Template compile/runtime errors

Import FormsModule (or use Reactive Forms)

Using attribute binding for real element state

[attr.disabled] doesn’t behave like [disabled] in all cases

Prefer property binding for element state

Trying to use two-way binding everywhere

Harder to reason about state flow in complex apps

Prefer one-way data flow + explicit events; use two-way mainly for form-like controls

These are the things seniors call out quickly.

Summary

Data binding connects TS state and HTML templates; Angular syncs the DOM via change detection.

Know the full surface area: interpolation, property, attr/class/style, event, and two-way binding.

Two-way binding is syntactic sugar: [(x)] = [x] + (xChange).

Interview nuance: property vs attribute binding, and when to use [attr.*], [class.*], [style.*].

Similar questions
Guides
Preparing for interviews?

Use the relevant interview-question hub first, then move into a concrete study plan before targeted company sets.