Pebellslearn by building
0%
RoadmapsMatrixCoursesSandboxSign in
Understand Binding  ›  Capstone Project

Two-way Binding

Two-way Binding 12 minTwo-way binding with the [( )] syntax combines a property binding and an event binding, so a form field or a child component's model input and a parent signal stay in sync.

Two-way Binding

The "Edit plant" form has a nickname box and a water-level slider. When the class changes the value, the field should show it; when the user types or drags, the class should know. That is data flowing both ways, and Angular has a shorthand for it.

The rule

Banana in a box: [( )] is a binding in plus an event out.

The shape [( )] looks like a banana inside a box, which is how most people remember the order: brackets outside, parentheses inside.

It is pure shorthand. For a property called level, Angular expands

<app-water-slider [(level)]="waterLevel" />

into a property binding and an event binding whose name is the property plus Change:

<app-water-slider [level]="waterLevel()" (levelChange)="waterLevel.set($event)" />

On your own components, declare the property with model(). It creates the input and the matching levelChange output in one go:

import { Component, model } from '@angular/core';

@Component({
  selector: 'app-water-slider',
  template: `<input type="range" min="0" max="100"
               [value]="level()" (input)="onSlide($event)" />`,
})
export class WaterSlider {
  level = model(0);

  onSlide(e: Event) {
    this.level.set(Number((e.target as HTMLInputElement).value));
  }
}

Unlike an input(), a model() is writable from inside the child, and each set sends the new value up.

On form fields, use ngModel from FormsModule:

@Component({
  imports: [FormsModule],
  template: `<input [(ngModel)]="nickname" />
             <p>Hello, {{ nickname() }}</p>`,
})
export class EditPlant {
  nickname = signal('');
}

Notice [(ngModel)]="nickname", not nickname(). Two-way binding needs the signal itself so it can both read and write it.

The shortcut

When you see [(x)], mentally unfold it into [x] and (xChange). Every question about two-way binding becomes a question about one ordinary input and one ordinary output.

Unfolding the nickname boxStep 1 of 4
  1. Unfold

    [(ngModel)]="nickname" becomes [ngModel]="nickname()" plus (ngModelChange)="nickname.set($event)".

Drills

Answer fast — trust the rule.

quick drill

Which is the correct two-way binding syntax?

quick drill

[(size)]="potSize" expands to [size] plus which event?

quick drill

How should a child declare a property that supports [(level)]?

quick drill

nickname = signal(''). Which binding works?

Remember

  • [(x)] is shorthand for [x] in plus (xChange) out.
  • Use model() for two-way props on your own components; ngModel plus FormsModule for form fields.
  • Trap: calling the signal inside the banana box; pass nickname, not nickname().

Practice — level up

0/1 solved

Write your answer, then compare it with a rubric and a model answer.

ExplainUnpack the banana

Greenhouse has a WaterSlider child component and a nickname text box. Explain what [(level)]="waterLevel" expands into, how WaterSlider should declare level, and what is needed for [(ngModel)] on the text box. Point out one mistake people make with signals here.