返回文章列表

Angular FormGroup:響應式表單

11 分鐘
前端Angular

Angular FormGroup:響應式表單

Angular 提供兩種表單處理方式:Template-driven Forms 和 Reactive Forms。

本篇介紹 Reactive Forms (響應式表單),它透過 FormGroupFormControlFormArray 在 TypeScript 中管理表單狀態,讓表單邏輯更容易測試和維護。


FormControl

FormControl 是響應式表單的基本單位,代表一個單一的輸入欄位。

TypeScript
import { Component } from '@angular/core';
import { FormControl, ReactiveFormsModule } from '@angular/forms';

@Component({
  standalone: true,
  imports: [ReactiveFormsModule],
  template: `
    <input [formControl]="name" placeholder="姓名" />
    <p>目前值:{{ name.value }}</p>
  `,
})
export class DemoComponent {
  name = new FormControl('');
}

讀取與設定值

TypeScript
// 讀取值
console.log(this.name.value);

// 設定值
this.name.setValue('Charmy');

// 部分更新 (FormControl 只有一個值,通常用 setValue)
this.name.patchValue('Charmy');

監聽值的變化

TypeScript
this.name.valueChanges.subscribe(value => {
  console.log(value);
});

FormGroup

FormGroup 將多個 FormControl 組合成一個群組,代表整個表單或表單的一部分。

TypeScript
import { Component } from '@angular/core';
import { FormGroup, FormControl, ReactiveFormsModule } from '@angular/forms';

@Component({
  standalone: true,
  imports: [ReactiveFormsModule],
  template: `
    <form [formGroup]="form">
      <input formControlName="name" placeholder="姓名" />
      <input formControlName="email" placeholder="Email" />
    </form>
    <p>表單值:{{ form.value | json }}</p>
  `,
})
export class RegisterComponent {
  form = new FormGroup({
    name: new FormControl(''),
    email: new FormControl(''),
  });
}

讀取與設定值

TypeScript
// 讀取整個表單的值
console.log(this.form.value); // { name: '', email: '' }

// 讀取單一欄位
console.log(this.form.get('name')?.value);

// 設定所有欄位的值
this.form.setValue({
  name: 'Charmy',
  email: 'charmy@example.com',
});

// 部分更新 (只更新指定欄位)
this.form.patchValue({
  name: 'Charmy',
});

表單狀態

TypeScript
this.form.valid   // 是否通過所有驗證
this.form.invalid // 是否有驗證錯誤
this.form.dirty   // 是否有欄位被修改過
this.form.touched // 是否有欄位被觸碰過 (失焦)
this.form.pristine // 是否尚未被修改

驗證

內建驗證器

Angular 的 Validators 提供常用的內建驗證規則:

TypeScript
import { FormGroup, FormControl, Validators } from '@angular/forms';

form = new FormGroup({
  name: new FormControl('', [
    Validators.required,
    Validators.minLength(2),
    Validators.maxLength(50),
  ]),
  email: new FormControl('', [
    Validators.required,
    Validators.email,
  ]),
  age: new FormControl('', [
    Validators.min(0),
    Validators.max(120),
  ]),
});

常用的內建驗證器:

驗證器說明
Validators.required必填
Validators.minLength(n)最少 n 個字元
Validators.maxLength(n)最多 n 個字元
Validators.emailEmail 格式
Validators.min(n)最小值 n
Validators.max(n)最大值 n
Validators.pattern(regex)符合正規表達式

顯示驗證錯誤

HTML
<form [formGroup]="form">
  <input formControlName="name" placeholder="姓名" />
  <div *ngIf="form.get('name')?.invalid && form.get('name')?.touched">
    <span *ngIf="form.get('name')?.errors?.['required']">姓名為必填</span>
    <span *ngIf="form.get('name')?.errors?.['minlength']">至少需要 2 個字元</span>
  </div>
</form>

自訂驗證器

自訂驗證器是一個函式,接收 AbstractControl,驗證失敗時回傳錯誤物件,驗證通過時回傳 null

TypeScript
import { AbstractControl, ValidationErrors } from '@angular/forms';

function noSpaceValidator(control: AbstractControl): ValidationErrors | null {
  if (control.value && control.value.includes(' ')) {
    return { noSpace: true };
  }
  return null;
}

使用自訂驗證器:

TypeScript
form = new FormGroup({
  username: new FormControl('', [
    Validators.required,
    noSpaceValidator,
  ]),
});

顯示自訂錯誤:

HTML
<span *ngIf="form.get('username')?.errors?.['noSpace']">
  使用者名稱不能包含空格
</span>

FormArray

FormArray 用來管理數量不固定的表單欄位,例如多個電話號碼、多個項目清單。

TypeScript
import { Component } from '@angular/core';
import { FormGroup, FormControl, FormArray, Validators, ReactiveFormsModule } from '@angular/forms';
import { NgFor } from '@angular/common';

@Component({
  standalone: true,
  imports: [ReactiveFormsModule, NgFor],
  template: `
    <form [formGroup]="form">
      <div formArrayName="phones">
        <div *ngFor="let phone of phones.controls; let i = index">
          <input [formControlName]="i" placeholder="電話號碼" />
          <button type="button" (click)="removePhone(i)">移除</button>
        </div>
      </div>
      <button type="button" (click)="addPhone()">新增電話</button>
    </form>
  `,
})
export class ContactComponent {
  form = new FormGroup({
    phones: new FormArray([
      new FormControl('', Validators.required),
    ]),
  });

  get phones() {
    return this.form.get('phones') as FormArray;
  }

  addPhone() {
    this.phones.push(new FormControl('', Validators.required));
  }

  removePhone(index: number) {
    this.phones.removeAt(index);
  }
}

表單提交與重置

提交表單

HTML
<form [formGroup]="form" (ngSubmit)="onSubmit()">
  <input formControlName="name" placeholder="姓名" />
  <input formControlName="email" placeholder="Email" />
  <button type="submit" [disabled]="form.invalid">送出</button>
</form>
TypeScript
onSubmit() {
  if (this.form.valid) {
    console.log(this.form.value);
    // 送出資料...
  }
}

重置表單

TypeScript
// 重置為初始空值
this.form.reset();

// 重置為指定值
this.form.reset({
  name: '',
  email: '',
});

reset 除了清空值,也會重置 dirtytouched 等狀態。


總結

Angular 響應式表單的三個核心類別:

  • FormControl:單一欄位,管理值與驗證狀態
  • FormGroup:多個欄位的群組,代表整個表單
  • FormArray:數量不固定的欄位陣列
FormControlFormGroupFormArray
用途單一輸入表單群組動態欄位
取值.value.value.value
設值setValuesetValue / patchValuepush / removeAt