【Angular】双方向バインディング([(ngModel)])とは?使い方を解説!

Angularでフォーム入力を扱う際にとても便利な機能が、双方向バインディング(Two-way Binding)です。[(ngModel)]という構文を使えば、テンプレートとコンポーネントのプロパティが自動で同期させることができます。

この記事では、[(ngModel)]による双方向バインディングについて、以下の内容をサンプルコード付きでわかりやすく解説します。

  • 双方向バインディングとは?
  • [(ngModel)]の使い方
  • ngModel[(ngModel)]の違い
  • (ngModelChange)とは
  • (ngModelChange)(input)の違い
  • [(ngModel)]の応用例(ラジオボタン・セレクトボックス)

双方向バインディングとは?

Angularにおける「双方向バインディング(Two-way Binding)」は、以下のように「テンプレート(HTML)」と「コンポーネント(TypeScript)」の間でデータがリアルタイムで同期される仕組みです。

  • テンプレート上で値を変更する → その値がコンポーネントに更新される
  • コンポーネント側で値を変更する → その値がテンプレートに更新される

片方向ではなく双方向のデータのやり取りを行っているため、「テンプレート(HTML)」と「コンポーネント(TypeScript)」の値が常に一致します。Angularでは、[(ngModel)]を使うことでこの双方向バインディングを簡単に実現できます。

[(ngModel)]の使い方

まず、[(ngModel)]を使うには、FormsModuleをアプリケーションに読み込む必要があります。そのため、まずapp.module.tsFormsModuleを追加しましょう。これを忘れるとAngularはngModelを認識できません。

// app.module.ts
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms'; // 追加
import { BrowserModule } from '@angular/platform-browser';

import { AppComponent } from './app.component';

@NgModule({
  declarations: [AppComponent],
  imports: [BrowserModule, FormsModule], // FormsModuleを追加
  bootstrap: [AppComponent],
})
export class AppModule {}

[(ngModel)]を用いたサンプルコードを以下に示します。

// app.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  template: `
    <input [(ngModel)]="inputText" type="text" />
    <p>入力された値: {{ inputText }}</p>
  `,
})
export class AppComponent {
  inputText = 'abc';
}

この例では、[(ngModel)]="inputText"と書くことで、inputText<input>の値がリアルタイムで同期されます。ユーザーが入力した内容はすぐにinputTextに反映され、テンプレート上の{{ inputText }}の部分も更新されます。

ngModelと[(ngModel)]の違い

[(ngModel)]は、プロパティバインディングとイベントバインディングの組み合わせです。

// 以下の2つは同じ動作をする
<input [(ngModel)]="inputText" />
<input [ngModel]="inputText" (ngModelChange)="inputText = $event" />
  • [ngModel]:プロパティバインディング(コンポーネント → テンプレートのデータバインディング)
  • (ngModelChange):イベントバインディング(テンプレート → コンポーネントのイベントバインディング)

これらをまとめて書けるのが[(ngModel)]です。

ngModelディレクティブを[(...)]で囲んでいる点に注目です。プロパティバインディングの[...]とイベントバインディングの(...)を組み合わせた[(...)]が双方向バインディングを表しています。

(ngModelChange)とは

(ngModelChange)は、ngModelディレクティブで定義されている独自のイベントです。入力値の変化を検知して、リアルタイムに処理を行いたいときに使用します。サンプルコードを以下に示します。

// app.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  template: `
    <input
      [ngModel]="inputText" (ngModelChange)="onTextChange($event)" type="text" />
    <p>入力された値: {{ inputText }}</p>
  `,
})
export class AppComponent {
  inputText = 'abc';

  onTextChange(value: string): void {
    console.log('変更された値:', value);
    this.inputText = value;
  }
}

上記のサンプルコードでは、入力値が変更されるたびにonTextChange()が呼び出して、入力された値をログに出力しています。なお、onTextChange()では、値の更新やログ出力だけでなく、バリデーションやAPI呼び出しなども可能です。

$eventオブジェクトはそのまま新しい入力値を表します。

(input)イベントを用いる場合、$event.target.valueで入力値を取得する必要がありますが、(ngModelChange)イベントを用いると$event.target.valueのような記述は不要です。

(ngModelChange)と(input)の違い

(input)イベントでも入力値を取得できます。以下は同じ処理を(input)で書き換えた例です。

// app.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  template: `
    <input [ngModel]="inputText" (input)="onInputChange($event)" type="text" />
    <p>入力された値: {{ inputText }}</p>
  `,
})
export class AppComponent {
  inputText = 'abc';

  onInputChange(event: Event): void {
    const value = (event.target as HTMLInputElement).value;
    console.log('入力イベント:', value);
    this.inputText = value;
  }
}

(ngModelChange)(input)の違いを以下に示します。

比較項目(ngModelChange)(input)
発火タイミングngModelの値が変更されたとき入力イベントが発生したとき
引数変更後の値
(stringなど)
Eventオブジェクト
(手動でvalueを取得)
使用の手軽さvalue取得が簡単
(直接渡ってくるため)
event.target.valueで取り出す必要がある
Angular連携ngModelとの組み合わせに特化より汎用的なDOMイベント

[(ngModel)]の応用例(ラジオボタン・セレクトボックス)

[(ngModel)]は、テキスト入力だけでなく、ラジオボタンやセレクトボックスでも活用できます。

ラジオボタンに活用した場合

// app.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  template: `
    <label>
      <input type="radio" [(ngModel)]="selectedGender" value="male" />
      男性
    </label>
    <label>
      <input type="radio" [(ngModel)]="selectedGender" value="female" />
      女性
    </label>

    <p>選択された性別: {{ selectedGender }}</p>
  `,
})
export class AppComponent {
  selectedGender = '';
}

セレクトボックスに活用した場合

// app.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  template: `
    <select [(ngModel)]="selectedColor">
      <option value="red">赤</option>
      <option value="blue">青</option>
      <option value="green">緑</option>
    </select>

    <p>選択された色: {{ selectedColor }}</p>
  `,
})
export class AppComponent {
  selectedColor = '';
}

本記事のまとめ

この記事では『双方向バインディング([(ngModel)])』について、以下の内容を説明しました。

  • [(ngModel)]は、テンプレートとコンポーネントのプロパティをリアルタイムに同期するための構文。
  • 使用するにはFormsModuleのインポートが必要。
  • [(ngModel)][ngModel](ngModelChange)の組み合わせ。
  • (input)イベントと使い分けることで、汎用的な制御も可能。
  • テキスト入力以外にも、ラジオボタンやセレクトボックスにも使える。

Angularでフォーム操作を行う際には、この[(ngModel)]を使いこなすことで、記述がシンプルになり、可読性や保守性も高まります。

お読み頂きありがとうございました。