Data Binding

4 forms of data binding markup

Each form has a direction—to the DOM, from the DOM, or in both directions.

Data Binding
<li>{{hero.name}}</li> <!-- interpolation -->

<app-hero-detail [hero]="selectedHero"></app-hero-detail> <!-- property binding -->
<li (click)="selectHero(hero)"></li> <!-- event binding -->

<input [(ngModel)]="hero.name"> <!-- 2 ways data binding -->

two-way binding

You often want to both display a data property and update that property when the user makes changes.

In two-way binding, a data property value flows to the input box from the component as with property binding. The user's changes also flow back to the component, resetting the property to the latest value, as with event binding.

Angular processes all data bindings once per JavaScript event cycle, from the root of the application component tree through all child components.

two-way binding = property binding + event binding

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

<!-- equals -->

<input [value]="hero.name" (input)="hero.name=$event.target.value">
Data Binding

That ngModel directive hides these onerous details behind its own ngModel input and ngModelChange output properties.

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

AlthoughngModelis a valid Angular directive, it isn't available by default. It belongs to the optionalFormsModuleand you must opt-in to using it.

The [(ngModel)] syntax can only set a data-bound property. If you need to do something more or something different, you can write the expanded form.

The following contrived example forces the input value to uppercase:

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

Customized two-way binding

The [(x)] syntax is easy to demonstrate when the element has a settable property called x and a corresponding event named xChange. Here's a SizerComponent that fits the pattern. It has a size value property and a companion sizeChange event:

src/app/sizer.component.ts

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

@Component({
  selector: 'app-sizer',
  template: `
  <div>
    <button (click)="dec()" title="smaller">-</button>
    <button (click)="inc()" title="bigger">+</button>
    <label [style.font-size.px]="size">FontSize: {{size}}px</label>
  </div>`
})
export class SizerComponent {
  @Input()  size: number | string;
  @Output() sizeChange = new EventEmitter<number>();

  dec() { this.resize(-1); }
  inc() { this.resize(+1); }

  resize(delta: number) {
    this.size = Math.min(40, Math.max(8, +this.size + delta));
    this.sizeChange.emit(this.size);
  }
}
src/app/app.component.html (two-way-1)

<app-sizer [(size)]="fontSizePx"></app-sizer>
<div [style.font-size.px]="fontSizePx">Resizable Text</div>

The two-way binding syntax is really just syntactic sugar for a property binding and an event binding. Angular desugars the SizerComponent binding into this:

src/app/app.component.html (two-way-2)

<app-sizer [size]="fontSizePx" (sizeChange)="fontSizePx=$event"></app-sizer>

It would be convenient to use two-way binding with HTML form elements like <input> and <select>. However, no native HTML element follows the x value and xChange event pattern.

Fortunately, the Angular NgModel directive is a bridge that enables two-way binding to form elements.

Last updated

Was this helpful?