使用筆電輸入表單資料的特寫畫面

← INSIGHTS & PERSPECTIVES | 前端開發

Angular Template-Driven Forms 教學:ngModel、驗證與自訂 Validator

介紹 Angular 模版驅動表單的雙向綁定、內建驗證器、自訂 Validator 與 ngSubmit 送出流程。

Angular 的模版驅動表單(Template-Driven Forms)把大部分邏輯寫在 HTML 模版裡,靠 `ngModel` 做雙向綁定、靠內建或自訂的 directive 做驗證。它跟後來的 Reactive Forms 是兩條不同路線,但在中小型表單、原型驗證這類場景,模版驅動表單少寫很多樣板程式碼,上手也快。

如何用 Event 從 template 傳資料給 Component?

不透過 `ngModel`,直接用 `(keyup)` 這類事件把 `$event` 傳回 component,是最原始也最直接的做法:

```html

{{values}}

```

在 component 裡透過 `event.target` 存取這個 `HTMLInputElement` 的資料(詳細規格見 MDN HTMLInputElement):

```ts

export class KeyUpComponent_v1 {

values = '';

onKey(event: any) {

this.values += event.target.value + ' | ';

}

}

```

如何啟用模版驅動表單?

模版驅動表單需要先在 `AppModule` 匯入 `FormsModule`,才能使用 `ngModel` 等相關指令:

```ts

import { NgModule } from '@angular/core';

import { BrowserModule } from '@angular/platform-browser';

import { FormsModule } from '@angular/forms'; // 使用模版驅動表單需要 import 這個模組

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

import { HeroFormComponent } from './hero-form/hero-form.component';

@NgModule({

imports: [

BrowserModule,

FormsModule, // 加上這個模組後,應用程式才能使用所有模版驅動表單功能,包括 ngModel

],

declarations: [AppComponent, HeroFormComponent],

providers: [],

bootstrap: [AppComponent],

})

export class AppModule {}

```

ngModel 如何做雙向綁定?

`[(ngModel)]` 是 Angular 的 banana-in-a-box 語法,把 input 的值跟 component 屬性雙向同步,使用者打字時 component 裡的值即時更新,反過來改 component 屬性也會反映到畫面上:

```html

<input

type="text"

class="form-control"

id="name"

required

[(ngModel)]="model.name"

name="name"

/>

你所輸入的資料是:{{ model.name }}

```

component 端只要準備好被綁定的屬性即可:

```ts

export class HeroFormComponent {

model = {};

}

```

ngModel 會附加哪些 CSS class?

`ngModel` 會依照 input 的互動狀態自動切換一組 class,這些 class 可以直接拿來做樣式或驗證訊息判斷:

狀態Class if trueClass if false
被點擊接觸過`ng-touched``ng-untouched`
值被改變`ng-dirty``ng-pristine`
值不符合驗證`ng-valid``ng-invalid`

在 input 上加 `#spy` 這個模版參考變數,可以即時觀察 class 名稱如何隨互動變化:

```html

<input

type="text"

class="form-control"

id="name"

required

[(ngModel)]="model.name"

name="name"

#spy

/>

{{ spy.className }}

```

如何顯示與隱藏驗證錯誤訊息?

要讓某個 input 支援驗證,它必須宣告 `ngModel`(不論有沒有做雙向綁定),或宣告 `formControlName` / `formControl` —— 沒有這三者之一,Angular 不會對這個欄位做任何驗證。

最基本的必填驗證只要加上 `required`:

```html

```

如果要顯示客製化的錯誤訊息,可以把 `ngModel` 匯出成模版變數,因為 `ngModel` directive 的 `exportAs` 值剛好就是 `ngModel`:

```html

<input

type="text"

class="form-control"

id="name"

required

[(ngModel)]="model.name"

name="name"

#name="ngModel"

/>

```

拿到 `#name="ngModel"` 這個變數後,就能用 `name.valid`、`name.pristine` 判斷是否顯示錯誤:

```html

Name is required

```

`pristine` 代表這個欄位還沒被使用者改變過。`[hidden]="name.valid || name.pristine"` 的意思是:只要欄位還沒填寫,或填寫的值合法,就不顯示錯誤訊息 —— 避免表單一載入就滿版紅字。

Angular 內建哪些表單驗證器?

`required` 是最常見的驗證 directive,完整用法可以查 RequiredValidator 的官方文件。要注意 `required` 不能直接用在 checkbox 上,checkbox 必填要換一個驗證器:

如何寫一個自訂的模版驅動表單驗證器?

內建驗證器不夠用時,可以自己寫一個實作 `Validator` 介面的 directive,透過 `NG_VALIDATORS` token 註冊進表單驗證流程:

```ts

@Directive({

selector: '[forbiddenName]',

providers: [

{ provide: NG_VALIDATORS, useExisting: ForbiddenValidatorDirective, multi: true },

],

})

export class ForbiddenValidatorDirective implements Validator {

@Input() forbiddenName: string;

validate(control: AbstractControl): { [key: string]: any } {

return this.forbiddenName

? forbiddenNameValidator(new RegExp(this.forbiddenName, 'i'))(control)

: null;

}

}

```

使用方式跟內建驗證器一樣,直接當 attribute 加在 input 上:

```html

<input

id="name"

name="name"

class="form-control"

required

minlength="2"

forbiddenName="bob"

[(ngModel)]="hero.name"

#name="ngModel"

/>

Name cannot be Bob.

```

資訊增益:Angular 官網早期範例把 `forbidden-name.directive.ts` 的 selector 誤寫成 `appForbiddenName`,實際上要用 `forbiddenName` 才能跟上面的 attribute 對起來,這個落差已經有人回報成 angular/angular#20206。照抄官方範例卻驗證不生效時,這往往就是原因。

ngSubmit 怎麼送出表單?

`<form>` 裡的 submit 按鈕按下後不會自己觸發任何事,Angular 會發出一個 `ngSubmit` 事件,需要自己在表單上註冊:

```html

```

把整個 `ngForm` 存進 `heroForm` 這個模版變數後,就能在 submit 按鈕上讀取表單目前是否通過驗證,決定要不要讓按鈕可以按:

```html

```

常見問題

QngModel 一定要搭配雙向綁定才能驗證嗎?

不用。只要 input 上有 `ngModel`(單純 `ngModel`,不寫成 `[(ngModel)]`),Angular 就會把它納入表單驗證,`formControlName` 或 `formControl` 也是同樣效果。

Q為什麼我的自訂 Validator directive 完全沒作用?

先檢查 selector 名稱是否跟 input 上寫的 attribute 完全一致,這正是官方範例曾經出過的落差(見上文 issue #20206);其次確認有把它加進 `NG_VALIDATORS` 並設 `multi: true`,否則會直接覆蓋掉其他驗證器。

Q`[hidden]="name.valid || name.pristine"` 這種寫法要注意什麼?

它只是控制錯誤訊息何時顯示的其中一種寫法,實務上要留意 `pristine` 只代表「使用者還沒動過」,如果表單一開始就帶入不合法的預設值,使用者不去碰它,錯誤訊息也不會跳出來,這種情況要另外處理。

參考資料

最後更新

本文最後更新於 2026-08-27。原文發布於 2018-01-04,內容保留原始的模版驅動表單教學與自訂驗證器範例,並重新整理為 GEO 適用的問答式段落。

延伸閱讀

關於作者 {#author}

Claire Chang | 企業 AI 導入與流程轉型顧問。專注於 AI Agent 架構設計、ERP 系統整合與企業 AI 治理。

首次發布:2018-01-04