Dev Study
← 解説「双方向バインディング — [(ngModel)] で入力と同期する」に戻る

サンプルコードで身につける: 双方向バインディング — [(ngModel)] で入力と同期する

解説で学んだ概念を、5つの具体例で確認します。素朴な例から実務寄りの例へと進みます。

1最小の [(ngModel)]

双方向バインディングのいちばん基本の形です。入力欄に文字を打つと name が更新され、name をクラス側で変えれば入力欄の表示も変わります。FormsModule を imports に入れるのを忘れないのがポイントです。

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

@Component({
  selector: 'app-nickname',
  imports: [FormsModule], // ngModel を使うために必須
  template:
    '<input [(ngModel)]="nickname">' +
    '<p>ニックネーム: {{ nickname }}</p>',
})
export class NicknameComponent {
  nickname = 'ぽんた';
}

2バナナ・イン・ア・ボックスを分解する

[(ngModel)] は、[ngModel] での値の流し込みと (ngModelChange) での変更の受け取りを1つにまとめた省略記法です。分解した形を一度見ておくと、双方向バインディングが魔法ではなく既習の2つの記法の合体だと分かります。

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

@Component({
  selector: 'app-banana-box',
  imports: [FormsModule],
  // 上の行と下の行はまったく同じ意味
  template:
    '<input [(ngModel)]="memo">' +
    '<input [ngModel]="memo" (ngModelChange)="memo = $event">',
})
export class BananaBoxComponent {
  memo = '';
}

3select 要素と同期する

双方向バインディングはテキスト入力だけでなく select にも使えます。選択を変えると selectedColor が更新され、初期値としてプロパティの値に一致する選択肢が最初から選ばれた状態になります。

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

@Component({
  selector: 'app-color-picker',
  imports: [FormsModule],
  template:
    '<select [(ngModel)]="selectedColor">' +
    '  <option value="red">赤</option>' +
    '  <option value="blue">青</option>' +
    '  <option value="green">緑</option>' +
    '</select>' +
    '<p>選択中: {{ selectedColor }}</p>',
})
export class ColorPickerComponent {
  selectedColor = 'blue'; // 初期表示から「青」が選ばれている
}

4チェックボックスと boolean を同期する

チェックボックスに [(ngModel)] を付けると、チェック状態が boolean のプロパティと同期します。その値をほかの要素の [disabled] に使えば、「同意したら送信ボタンが押せる」という定番のUIが数行で書けます。

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

@Component({
  selector: 'app-agreement',
  imports: [FormsModule],
  template:
    '<label>' +
    '  <input type="checkbox" [(ngModel)]="agreed"> 利用規約に同意する' +
    '</label>' +
    '<button [disabled]="!agreed">送信</button>',
})
export class AgreementComponent {
  agreed = false; // チェックすると true になり、ボタンが押せる
}

5シグナルと組み合わせる

状態をシグナルで持っている場合は、[ngModel] にシグナルの値を渡し、(ngModelChange) で set を呼ぶ分解形が確実です。入力値を computed の派生値(ここでは文字数)につなげられるのがシグナルと組み合わせる利点です。

import { Component, signal, computed } from '@angular/core';
import { FormsModule } from '@angular/forms';

@Component({
  selector: 'app-tweet-box',
  imports: [FormsModule],
  // シグナルは「読み: text()」「書き: text.set($event)」で分解してつなぐ
  template:
    '<textarea [ngModel]="text()" (ngModelChange)="text.set($event)"></textarea>' +
    '<p>残り {{ remaining() }} 文字</p>',
})
export class TweetBoxComponent {
  text = signal('');
  remaining = computed(() => 140 - this.text().length);
}