サンプルコードで身につける: リアクティブフォーム — FormControl と FormGroup
解説で学んだ概念を、5つの具体例で確認します。素朴な例から実務寄りの例へと進みます。
1最小の FormControl
リアクティブフォームの最小単位は、1つの入力欄を表す FormControl です。new FormControl('') で作り、value で現在の値を読み、setValue() でプログラムから値を設定できます。フォームの値がTypeScript側のオブジェクトとして手元にある、という感覚をまずこの1つで掴んでください。
import { Component } from '@angular/core';
import { FormControl, ReactiveFormsModule } from '@angular/forms';
@Component({
selector: 'app-nickname',
imports: [ReactiveFormsModule],
// [formControl] で入力欄と FormControl を結び付ける
template:
'<input [formControl]="nickname" placeholder="ニックネーム">' +
'<p>入力中の値: {{ nickname.value }}</p>',
})
export class NicknameComponent {
nickname = new FormControl('');
reset() {
// プログラム側からも値を設定できる
this.nickname.setValue('ゲスト');
}
}
2FormGroup でログインフォームを組み立てる
複数の FormControl は FormGroup でひとまとめにし、1つのフォームとして扱います。テンプレートでは <form [formGroup]="form"> で全体を、formControlName="email" で各入力欄を結び付けます。ログインフォームのような「複数項目で1つの意味を持つ」入力は、この形が基本形です。
import { Component } from '@angular/core';
import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
@Component({
selector: 'app-login',
imports: [ReactiveFormsModule],
// [formGroup] でフォーム全体、formControlName で各項目を対応させる
template:
'<form [formGroup]="form" (ngSubmit)="login()">' +
' <input formControlName="email" placeholder="メール">' +
' <input formControlName="password" type="password">' +
' <button type="submit">ログイン</button>' +
'</form>',
})
export class LoginComponent {
form = new FormGroup({
email: new FormControl(''),
password: new FormControl(''),
});
login() {
console.log('送信内容:', this.form.value);
}
}
3値の取得 — value と getRawValue の違い
フォームの値は form.value でいつでもオブジェクトとして取り出せます。ただし disable() で無効化したコントロールは value に含まれなくなるため、無効な項目も含めた全部の値が欲しいときは getRawValue() を使います。「画面上は編集不可だが送信には含めたい」項目があるフォームで、この違いが効いてきます。
import { FormControl, FormGroup } from '@angular/forms';
const form = new FormGroup({
name: new FormControl('田中'),
plan: new FormControl('premium'),
});
// plan を編集不可にする(契約プランは画面から変更させない、など)
form.get('plan')?.disable();
// value は無効化したコントロールを含まない
console.log(form.value);
// => { name: '田中' }
// getRawValue() は無効化したものも含めた全項目を返す
console.log(form.getRawValue());
// => { name: '田中', plan: 'premium' }
4valueChanges で入力の変化を購読する
各コントロールやフォーム全体は valueChanges という Observable を持ち、値が変わるたびに新しい値が流れてきます。subscribe すると「入力に反応して何かする」処理をクラス側に書けます。検索キーワードの入力に合わせて候補を出す、といったリアルタイムな挙動の土台になります。
import { Component } from '@angular/core';
import { FormControl, ReactiveFormsModule } from '@angular/forms';
@Component({
selector: 'app-search-box',
imports: [ReactiveFormsModule],
template: '<input [formControl]="keyword" placeholder="検索">',
})
export class SearchBoxComponent {
keyword = new FormControl('');
constructor() {
// 値が変わるたびに新しい値が流れてくる
this.keyword.valueChanges.subscribe((value) => {
console.log('入力が変わりました:', value);
// ここで検索候補の取得などを行う
});
}
}
5setValue と patchValue の違い
FormGroup へまとめて値を入れる方法は2つあります。setValue() は全項目を漏れなく渡す必要があり、1つでも欠けるとエラーになる厳密な方式です。patchValue() は渡した項目だけを部分的に更新できるので、「サーバーから取った一部の値だけ反映したい」編集画面などで便利です。
import { FormControl, FormGroup } from '@angular/forms';
const form = new FormGroup({
name: new FormControl(''),
email: new FormControl(''),
});
// setValue は全項目を渡す。欠けているとエラーになる
form.setValue({ name: '佐藤', email: 'sato@example.com' });
// patchValue は一部だけの更新ができる
form.patchValue({ name: '鈴木' });
console.log(form.value);
// => { name: '鈴木', email: 'sato@example.com' }
// form.setValue({ name: '高橋' });
// => email が無いので実行時エラーになる