Skip to content
beginner Phase 4 · Bindings, Structural Directives, and Events

Two-Way Binding with ngModel

Sync a component field with an input using banana-in-a-box syntax.

30m
0 problems
Topic Progress 0%

The Banana-in-a-Box Syntax

Two-way binding combines property binding [ ] and event binding ( ) into a single syntax: [( )]. It keeps a component field and a form input in sync.

Basic ngModel Usage

@Component({
  selector: 'app-name-input',
  template: `
    <input [(ngModel)]="name" placeholder="Enter name">
    <p>Hello, {{ name }}!</p>
  `,
})
export class NameInputComponent {
  name = '';
}

When the user types in the input, name updates automatically. When name changes in the component, the input updates automatically.

What [(ngModel)] Really Means

<!-- This: -->
<input [(ngModel)]="name">

<!-- Is shorthand for this: -->
<input [ngModel]="name" (ngModelChange)="name = $event">

Requirements

ngModel requires FormsModule to be imported:

import { FormsModule } from '@angular/forms';

@Component({
  selector: 'app-example',
  imports: [FormsModule],
  template: `<input [(ngModel)]="value">`,
})
export class ExampleComponent {
  value = '';
}

Multiple Fields

@Component({
  selector: 'app-user-form',
  imports: [FormsModule],
  template: `
    <input [(ngModel)]="user.firstName" placeholder="First name">
    <input [(ngModel)]="user.lastName" placeholder="Last name">
    <input [(ngModel)]="user.email" type="email" placeholder="Email">
    <input [(ngModel)]="user.age" type="number" placeholder="Age">

    <pre>{{ user | json }}</pre>
  `,
})
export class UserFormComponent {
  user = { firstName: '', lastName: '', email: '', age: 0 };
}

Two-Way Binding with Custom Components

You can implement two-way binding in your own components using model() or @Input + @Output.

Using model() (Modern)

@Component({
  selector: 'app-counter',
  template: `
    <button (click)="decrement()">-</button>
    <span>{{ count() }}</span>
    <button (click)="increment()">+</button>
  `,
})
export class CounterComponent {
  count = model<number>(0);

  increment(): void {
    this.count.update(c => c + 1);
  }

  decrement(): void {
    this.count.update(c => c - 1);
  }
}
<app-counter [(count)]="parentCount"></app-counter>

Using @Input + @Output (Traditional)

@Component({
  selector: 'app-rating',
  template: `
    <span *ngFor="let star of [1,2,3,4,5]"
          (click)="setRating(star)"
          [class.filled="star <= rating">
      ★
    </span>
  `,
})
export class RatingComponent {
  @Input() rating = 0;
  @Output() ratingChange = new EventEmitter<number>();

  setRating(value: number): void {
    this.rating = value;
    this.ratingChange.emit(value);
  }
}
<app-rating [(rating)]="productRating"></app-rating>

Template-Driven vs Reactive Forms

Feature Template-Driven (ngModel) Reactive Forms
Setup Minimal — just import FormsModule More boilerplate
Validation Template directives Component class
Dynamic fields Difficult Easy (FormArray)
Testing Harder (DOM-dependent) Easier (class-based)
Complex logic Harder to manage Clean separation
Best for Simple forms Complex forms

For complex forms, prefer reactive forms (covered in Phase 524).

Quiz

1. What is the syntax for two-way binding?

Question 1 options

2. What module must be imported to use ngModel?

Question 2 options

3. What is the modern signal-based way to create a two-way bindable component?

Question 3 options

Flashcards

Question

What is two-way binding syntax?

Answer

[(property)] — banana-in-a-box. Combines [property] binding and (event) binding.

Question

What module does ngModel need?

Answer

FormsModule from @angular/forms.

Question

How do you make a custom two-way bindable component?

Answer

Use model() signal, or @Input + @Output with matching Change suffix (e.g., rating + ratingChange).

Revision Notes

Key Takeaways

  • 1. [(ngModel)] syncs component fields with form inputs
  • 2. FormsModule must be imported for ngModel
  • 3. model() creates two-way bindable signals for custom components
  • 4. Prefer reactive forms for complex scenarios

Interview Tips

  • • Explain that [(ngModel)] is shorthand for [ngModel] + (ngModelChange)
  • • Be ready to compare template-driven vs reactive forms
  • • Mention model() as the modern approach for custom components

Cheat Sheet

Two-Way Binding Cheat Sheet

Banana-in-a-box:

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

Shorthand for:

<input [ngModel]="name" (ngModelChange)="name = $event">

Custom component (model):

count = model<number>(0);
<app-counter [(count)]="parentCount"></app-counter>

Custom component (traditional):

@Input() rating = 0;
@Output() ratingChange = new EventEmitter<number>();

Continue Learning