Angular 表單簡介
Introduction to forms in Angular
用表單處理使用者輸入是許多常見應用的基礎功能。 應用透過表單來讓使用者登入、修改個人檔案、輸入敏感資訊以及執行各種資料輸入任務。
Handling user input with forms is the cornerstone of many common applications. Applications use forms to enable users to log in, to update a profile, to enter sensitive information, and to perform many other data-entry tasks.
Angular 提供了兩種不同的方法來透過表單處理使用者輸入:響應式表單和範本驅動表單。 兩者都從檢視中捕獲使用者輸入事件、驗證使用者輸入、建立表單模型、修改資料模型,並提供追蹤這些更改的途徑。
Angular provides two different approaches to handling user input through forms: reactive and template-driven. Both capture user input events from the view, validate the user input, create a form model and data model to update, and provide a way to track changes.
本指南提供的資訊可以幫你確定哪種方式最適合你的情況。它介紹了這兩種方法所用的公共構成要素,還總結了兩種方式之間的關鍵區別,並在建立、資料流和測試等不同的情境下展示了這些差異。
This guide provides information to help you decide which type of form works best for your situation. It introduces the common building blocks used by both approaches. It also summarizes the key differences between the two approaches, and demonstrates those differences in the context of setup, data flow, and testing.
先決條件
Prerequisites
本指南假設你對以下內容有基本的瞭解。
This guide assumes that you have a basic understanding of the following.
TypeScript和 HTML5 程式設計。
TypeScript and HTML5 programming.
Angular 的應用設計基礎,就像Angular Concepts 中描述的那樣。
Angular app-design fundamentals, as described in Angular Concepts.
Angular 範本語法的基礎知識。
The basics of Angular template syntax.
選擇一種方法
Choosing an approach
響應式表單和範本驅動表單以不同的方式處理和管理表單資料。每種方法都有各自的優點。
Reactive forms and template-driven forms process and manage form data differently. Each approach offers different advantages.
響應式表單提供對底層表單物件模型直接、顯式的訪問。它們與範本驅動表單相比,更加健壯:它們的可擴充套件性、可複用性和可測試性都更高。如果表單是你的應用程式的關鍵部分,或者你已經在使用響應式表單來建構應用,那就使用響應式表單。
Reactive forms provide direct, explicit access to the underlying forms object model. Compared to template-driven forms, they are more robust: they're more scalable, reusable, and testable. If forms are a key part of your application, or you're already using reactive patterns for building your application, use reactive forms.
範本驅動表單依賴範本中的指令來建立和操作底層的物件模型。它們對於嚮應用新增一個簡單的表單非常有用,比如電子郵件列表登錄檔單。它們很容易新增到應用中,但在擴充套件性方面不如響應式表單。如果你有可以只在範本中管理的非常基本的表單需求和邏輯,那麼範本驅動表單就很合適。
Template-driven forms rely on directives in the template to create and manipulate the underlying object model. They are useful for adding a simple form to an app, such as an email list signup form. They're easy to add to an app, but they don't scale as well as reactive forms. If you have very basic form requirements and logic that can be managed solely in the template, template-driven forms could be a good fit.
關鍵差異
Key differences
下表總結了響應式表單和範本驅動表單之間的一些關鍵差異。
The table below summarizes the key differences between reactive and template-driven forms.
響應式 Reactive | 範本驅動 Template-driven | |
---|---|---|
顯式的,在元件類別中建立 Explicit, created in component class | 隱式的,由指令建立 Implicit, created by directives | |
結構化和不可變的 Structured and immutable | 非結構化和可變的 Unstructured and mutable | |
可預測性 Predictability | 同步 Synchronous | 非同步 Asynchronous |
函式 Functions | 指令 Directives |
可延展性
Scalability
如果表單是應用程式的核心部分,那麼可延展性就非常重要。能夠跨元件複用表單模型是至關重要的。
If forms are a central part of your application, scalability is very important. Being able to reuse form models across components is critical.
響應式表單比範本驅動表單更有可延展性。它們提供對底層表單 API 的直接訪問,以及對表單資料模型的同步訪問,從而可以更輕鬆地建立大型表單。響應式表單需要較少的測試設定,測試時不需要深入理解變更檢測,就能正確測試表單更新和驗證。
Reactive forms are more scalable than template-driven forms. They provide direct access to the underlying form API, and synchronous access to the form data model, making creating large-scale forms easier. Reactive forms require less setup for testing, and testing does not require deep understanding of change detection to properly test form updates and validation.
範本驅動表單專注於簡單的場景,可複用性沒那麼高。它們抽象出了底層表單 API,並且只提供對表單資料模型的非同步訪問。對範本驅動表單的這種抽象也會影響測試。測試程式非常依賴於手動觸發變更檢測才能正常執行,並且需要進行更多設定工作。
Template-driven forms focus on simple scenarios and are not as reusable. They abstract away the underlying form API, and provide only asynchronous access to the form data model. The abstraction of template-driven forms also affects testing. Tests are deeply reliant on manual change detection execution to run properly, and require more setup.
建立表單模型
Setting up the form model
響應式表單和範本驅動型表單都會追蹤使用者與之互動的表單輸入元素和元件模型中的表單資料之間的值變更。這兩種方法共享同一套底層建構塊,只在如何建立和管理常用表單控制元件實例方面有所不同。
Both reactive and template-driven forms track value changes between the form input elements that users interact with and the form data in your component model. The two approaches share underlying building blocks, but differ in how you create and manage the common form-control instances.
常用表單基礎類別
Common form foundation classes
響應式表單和範本驅動表單都建立在下列基礎類別之上。
Both reactive and template-driven forms are built on the following base classes.
FormControl
實例用於追蹤單個表單控制元件的值和驗證狀態。FormControl
tracks the value and validation status of an individual form control.FormGroup
用於追蹤一個表單控制元件組的值和狀態。FormGroup
tracks the same values and status for a collection of form controls.FormArray
用於追蹤表單控制元件陣列的值和狀態。FormArray
tracks the same values and status for an array of form controls.ControlValueAccessor
用於在 Angular 的FormControl
實例和原生 DOM 元素之間建立一個橋樑。ControlValueAccessor
creates a bridge between AngularFormControl
instances and native DOM elements.
建立響應式表單
Setup in reactive forms
對於響應式表單,你可以直接在元件類別中定義表單模型。[formControl]
指令會透過內部值訪問器來把顯式建立的 FormControl
實例與檢視中的特定表單元素聯絡起來。
With reactive forms, you define the form model directly in the component class. The [formControl]
directive links the explicitly created FormControl
instance to a specific form element in the view, using an internal value accessor.
下面的元件使用響應式表單為單個控制元件實現了一個輸入欄位。在這個例子中,表單模型是 FormControl
實例。
The following component implements an input field for a single control, using reactive forms. In this example, the form model is the FormControl
instance.
import { Component } from '@angular/core';
import { FormControl } from '@angular/forms';
@Component({
selector: 'app-reactive-favorite-color',
template: `
Favorite Color: <input type="text" [formControl]="favoriteColorControl">
`
})
export class FavoriteColorComponent {
favoriteColorControl = new FormControl('');
}
圖 1 展示了在響應式表單中,表單模型是如何成為事實之源(source of truth)的。它透過輸入元素上的 [formControl]
指令,在任何給定的時間點提供表單元素的值和狀態。
Figure 1 shows how, in reactive forms, the form model is the source of truth; it provides the value and status of the form element at any given point in time, through the [formControl]
directive on the input element.
圖 1. 在響應式表單中直接訪問表單模型
Figure 1. Direct access to forms model in a reactive form.

建立範本驅動表單
Setup in template-driven forms
在範本驅動表單中,表單模型是隱式的,而不是顯式的。指令 NgModel
為指定的表單元素建立並管理一個 FormControl
實例。
In template-driven forms, the form model is implicit, rather than explicit. The directive NgModel
creates and manages a FormControl
instance for a given form element.
下面的元件使用範本驅動表單為單個控制元件實現了同樣的輸入欄位。
The following component implements the same input field for a single control, using template-driven forms.
import { Component } from '@angular/core';
@Component({
selector: 'app-template-favorite-color',
template: `
Favorite Color: <input type="text" [(ngModel)]="favoriteColor">
`
})
export class FavoriteColorComponent {
favoriteColor = '';
}
在範本驅動表單中,其事實之源就是範本。你沒有對 FormControl
實例的直接程式設計訪問,如圖 2 所示。
In a template-driven form the source of truth is the template. You do not have direct programmatic access to the FormControl
instance, as shown in Figure 2.
圖 2. 範本驅動表單中對表單模型的間接訪問。
Figure 2. Indirect access to forms model in a template-driven form.

表單中的資料流
Data flow in forms
當應用包含一個表單時,Angular 必須讓該檢視與元件模型保持同步,並讓元件模型與檢視保持同步。當用戶透過檢視更改值並進行選擇時,新值必須反映在資料模型中。同樣,當程式邏輯改變資料模型中的值時,這些值也必須反映到檢視中。
When an application contains a form, Angular must keep the view in sync with the component model and the component model in sync with the view. As users change values and make selections through the view, the new values must be reflected in the data model. Similarly, when the program logic changes values in the data model, those values must be reflected in the view.
響應式表單和範本驅動表單在處理來自使用者或程式化變更時的資料處理方式上有所不同。下面的這些示意圖會以上面定義的 favorite-color
輸入欄位為例,分別說明兩種表單各自的資料流。
Reactive and template-driven forms differ in how they handle data flowing from the user or from programmatic changes. The following diagrams illustrate both kinds of data flow for each type of form, using the favorite-color input field defined above.
響應式表單中的資料流
Data flow in reactive forms
在響應式表單中,檢視中的每個表單元素都直接連結到一個表單模型(FormControl
實例)。 從檢視到模型的修改以及從模型到檢視的修改都是同步的,而且不依賴於 UI 的渲染方式。
In reactive forms each form element in the view is directly linked to the form model (a FormControl
instance). Updates from the view to the model and from the model to the view are synchronous and do not depend on how the UI is rendered.
這個檢視到模型的示意圖展示了當輸入欄位的值發生變化時資料是如何從檢視開始,經過下列步驟進行流動的。
The view-to-model diagram shows how data flows when an input field's value is changed from the view through the following steps.
終端使用者在輸入框元素中鍵入了一個值,這裡是 "Blue"。
The user types a value into the input element, in this case the favorite color Blue.
這個輸入框元素會發出一個帶有最新值的 "input" 事件。
The form input element emits an "input" event with the latest value.
這個控制元件值訪問器
ControlValueAccessor
會監聽表單輸入框元素上的事件,並立即把新值傳給FormControl
實例。The control value accessor listening for events on the form input element immediately relays the new value to the
FormControl
instance.FormControl
實例會透過valueChanges
這個可觀察物件發出這個新值。The
FormControl
instance emits the new value through thevalueChanges
observable.valueChanges
的任何一個訂閱者都會收到這個新值。Any subscribers to the
valueChanges
observable receive the new value.

這個模型到檢視的示意圖體現了程式中對模型的修改是如何透過下列步驟傳播到檢視中的。
The model-to-view diagram shows how a programmatic change to the model is propagated to the view through the following steps.
favoriteColorControl.setValue()
方法被呼叫,它會更新這個FormControl
的值。The user calls the
favoriteColorControl.setValue()
method, which updates theFormControl
value.FormControl
實例會透過valueChanges
這個可觀察物件發出新值。The
FormControl
instance emits the new value through thevalueChanges
observable.valueChanges
的任何訂閱者都會收到這個新值。Any subscribers to the
valueChanges
observable receive the new value.該表單輸入框元素上的控制元件值訪問器會把控制元件更新為這個新值。
The control value accessor on the form input element updates the element with the new value.

範本驅動表單中的資料流
Data flow in template-driven forms
在範本驅動表單中,每一個表單元素都是和一個負責管理內部表單模型的指令關聯起來的。
In template-driven forms, each form element is linked to a directive that manages the form model internally.
這個檢視到模型的圖表展示了當輸入欄位的值發生變化時,資料流是如何從檢視開始經過下列步驟進行流動的。
The view-to-model diagram shows how data flows when an input field's value is changed from the view through the following steps.
終端使用者在輸入框元素中敲 "Blue"。
The user types Blue into the input element.
該輸入框元素會發出一個 "input" 事件,帶著值 "Blue"。
The input element emits an "input" event with the value Blue.
附著在該輸入框上的控制元件值訪問器會觸發
FormControl
實例上的setValue()
方法。The control value accessor attached to the input triggers the
setValue()
method on theFormControl
instance.FormControl
實例透過valueChanges
這個可觀察物件發出新值。The
FormControl
instance emits the new value through thevalueChanges
observable.valueChanges
的任何訂閱者都會收到新值。Any subscribers to the
valueChanges
observable receive the new value.控制元件值訪問器
ControlValueAccessory
還會呼叫NgModel.viewToModelUpdate()
方法,它會發出一個ngModelChange
事件。The control value accessor also calls the
NgModel.viewToModelUpdate()
method which emits anngModelChange
event.由於該元件範本雙向資料繫結到了
favoriteColor
,元件中的favoriteColor
屬性就會修改為ngModelChange
事件所發出的值("Blue")。Because the component template uses two-way data binding for the
favoriteColor
property, thefavoriteColor
property in the component is updated to the value emitted by thengModelChange
event (Blue).

這個模型到檢視的示意圖展示了當 favoriteColor
從藍變到紅時,資料是如何經過如下步驟從模型流動到檢視的。
The model-to-view diagram shows how data flows from model to view when the favoriteColor
changes from Blue to Red, through the following steps
元件中修改了
favoriteColor
的值。The
favoriteColor
value is updated in the component.變更檢測開始。
Change detection begins.
在變更檢測期間,由於這些輸入框之一的值發生了變化,Angular 就會呼叫
NgModel
指令上的ngOnChanges
生命週期鉤子。During change detection, the
ngOnChanges
lifecycle hook is called on theNgModel
directive instance because the value of one of its inputs has changed.ngOnChanges()
方法會把一個非同步任務排入佇列,以設定內部FormControl
實例的值。The
ngOnChanges()
method queues an async task to set the value for the internalFormControl
instance.變更檢測完成。
Change detection completes.
在下一個檢測週期,用來為
FormControl
實例賦值的任務就會執行。On the next tick, the task to set the
FormControl
instance value is executed.FormControl
實例透過可觀察物件valueChanges
發出最新值。The
FormControl
instance emits the latest value through thevalueChanges
observable.valueChanges
的任何訂閱者都會收到這個新值。Any subscribers to the
valueChanges
observable receive the new value.控制元件值訪問器
ControlValueAccessor
會使用favoriteColor
的最新值來修改表單的輸入框元素。The control value accessor updates the form input element in the view with the latest
favoriteColor
value.

資料模型的可變性
Mutability of the data model
變更追蹤的方法對應用的效率有著重要影響。
The change-tracking method plays a role in the efficiency of your application.
響應式表單透過以不可變的資料結構提供資料模型,來保持資料模型的純粹性。每當在資料模型上觸發更改時,
FormControl
實例都會返回一個新的資料模型,而不會更新現有的資料模型。這使你能夠透過該控制元件的可觀察物件追蹤對資料模型的唯一更改。這讓變更檢測更有效率,因為它只需在唯一性更改(譯註:也就是物件參考發生變化)時進行更新。由於資料更新遵循響應式模式,因此你可以把它和可觀察物件的各種運算子整合起來以轉換資料。Reactive forms keep the data model pure by providing it as an immutable data structure. Each time a change is triggered on the data model, the
FormControl
instance returns a new data model rather than updating the existing data model. This gives you the ability to track unique changes to the data model through the control's observable. Change detection is more efficient because it only needs to update on unique changes. Because data updates follow reactive patterns, you can integrate with observable operators to transform data.範本驅動的表單依賴於可變性和雙向資料繫結,可以在範本中做出更改時更新元件中的資料模型。由於使用雙向資料繫結時沒有用來對資料模型進行追蹤的唯一性更改,因此變更檢測在需要確定何時更新時效率較低。
Template-driven forms rely on mutability with two-way data binding to update the data model in the component as changes are made in the template. Because there are no unique changes to track on the data model when using two-way data binding, change detection is less efficient at determining when updates are required.
前面那些使用 favorite-color
輸入元素的例子就示範了這種差異。
The difference is demonstrated in the previous examples that use the favorite-color input element.
對於響應式表單,當控制元件值更新時,
FormControl
的實例總會返回一個新值。With reactive forms, the
FormControl
instance always returns a new value when the control's value is updated.對於範本驅動表單,
favorite-color
屬性總會被修改為新值。With template-driven forms, the favorite color property is always modified to its new value.
表單驗證
Form validation
驗證是管理任何表單時必備的一部分。無論你是要檢查必填項,還是查詢外部 API 來檢查使用者名稱是否已存在,Angular 都會提供一組內建的驗證器,以及建立自訂驗證器所需的能力。
Validation is an integral part of managing any set of forms. Whether you're checking for required fields or querying an external API for an existing username, Angular provides a set of built-in validators as well as the ability to create custom validators.
響應式表單把自訂驗證器定義成函式,它以要驗證的控制元件作為引數。
Reactive forms define custom validators as functions that receive a control to validate.
範本驅動表單和範本指令緊密相關,並且必須提供包裝了驗證函式的自訂驗證器指令。
Template-driven forms are tied to template directives, and must provide custom validator directives that wrap validation functions.
要了解驗證器的更多知識,參閱表單驗證。
For more information, see Form Validation.
測試
Testing
測試在複雜的應用程式中也起著重要的作用。當驗證你的表單功能是否正確時,更簡單的測試策略往往也更有用。測試響應式表單和範本驅動表單的差別之一在於它們是否需要渲染 UI 才能基於表單控制元件和表單欄位變化來執行斷言。下面的例子示範了使用響應式表單和範本驅動表單時表單的測試過程。
Testing plays a large part in complex applications. A simpler testing strategy is useful when validating that your forms function correctly. Reactive forms and template-driven forms have different levels of reliance on rendering the UI to perform assertions based on form control and form field changes. The following examples demonstrate the process of testing forms with reactive and template-driven forms.
測試響應式表單
Testing reactive forms
響應式表單提供了相對簡單的測試策略,因為它們能提供對表單和資料模型的同步訪問,而且不必渲染 UI 就能測試它們。在這些測試中,控制元件和資料是透過控制元件進行查詢和操縱的,不需要和變更檢測週期打交道。
Reactive forms provide a relatively easy testing strategy because they provide synchronous access to the form and data models, and they can be tested without rendering the UI. In these tests, status and data are queried and manipulated through the control without interacting with the change detection cycle.
下面的測試利用前面例子中的 "喜歡的顏色" 元件來驗證響應式表單中的 "從檢視到模型" 和 "從模型到檢視" 資料流。
The following tests use the favorite-color components from previous examples to verify the view-to-model and model-to-view data flows for a reactive form.
驗證“從檢視到模型”的資料流
Verifying view-to-model data flow
第一個例子執行了下列步驟來驗證“從檢視到模型”資料流。
The first example performs the following steps to verify the view-to-model data flow.
查詢表單輸入框元素的檢視,並為測試建立自訂的 "input" 事件
Query the view for the form input element, and create a custom "input" event for the test.
把輸入的新值設定為 Red,並在表單輸入元素上排程 "input" 事件。
Set the new value for the input to Red, and dispatch the "input" event on the form input element.
斷言該元件的
favoriteColorControl
的值與來自輸入框的值是匹配的。Assert that the component's
favoriteColorControl
value matches the value from the input.
it('should update the value of the input field', () => {
const input = fixture.nativeElement.querySelector('input');
const event = createNewEvent('input');
input.value = 'Red';
input.dispatchEvent(event);
expect(fixture.componentInstance.favoriteColorControl.value).toEqual('Red');
});
下一個例子執行了下列步驟來驗證“從模型到檢視”資料流。
The next example performs the following steps to verify the model-to-view data flow.
使用
favoriteColorControl
這個FormControl
實例來設定新值。Use the
favoriteColorControl
, aFormControl
instance, to set the new value.查詢表單中輸入框的檢視。
Query the view for the form input element.
斷言控制元件上設定的新值與輸入中的值是匹配的。
Assert that the new value set on the control matches the value in the input.
it('should update the value in the control', () => {
component.favoriteColorControl.setValue('Blue');
const input = fixture.nativeElement.querySelector('input');
expect(input.value).toBe('Blue');
});
測試範本驅動表單
Testing template-driven forms
使用範本驅動表單編寫測試就需要詳細瞭解變更檢測過程,以及指令在每個變更檢測週期中如何執行,以確保在正確的時間查詢、測試或更改元素。
Writing tests with template-driven forms requires a detailed knowledge of the change detection process and an understanding of how directives run on each cycle to ensure that elements are queried, tested, or changed at the correct time.
下面的測試使用了以前的 "喜歡的顏色" 元件,來驗證範本驅動表單的 "從檢視到模型" 和 "從模型到檢視" 資料流。
The following tests use the favorite color components mentioned earlier to verify the data flows from view to model and model to view for a template-driven form.
下面的測試驗證了 "從檢視到模型" 資料流:
The following test verifies the data flow from view to model.
it('should update the favorite color in the component', fakeAsync(() => {
const input = fixture.nativeElement.querySelector('input');
const event = createNewEvent('input');
input.value = 'Red';
input.dispatchEvent(event);
fixture.detectChanges();
expect(component.favoriteColor).toEqual('Red');
}));
這個 "檢視到模型" 測試的執行步驟如下:
Here are the steps performed in the view to model test.
查詢表單輸入元素中的檢視,並為測試建立自訂 "input" 事件。
Query the view for the form input element, and create a custom "input" event for the test.
把輸入框的新值設定為 Red,並在表單輸入框元素上派發 "input" 事件。
Set the new value for the input to Red, and dispatch the "input" event on the form input element.
透過測試夾具(Fixture)來執行變更檢測。
Run change detection through the test fixture.
斷言該元件
favoriteColor
屬性的值與來自輸入框的值是匹配的。Assert that the component
favoriteColor
property value matches the value from the input.
下面的測試驗證了 "從模型到檢視" 的資料流:
The following test verifies the data flow from model to view.
it('should update the favorite color on the input field', fakeAsync(() => {
component.favoriteColor = 'Blue';
fixture.detectChanges();
tick();
const input = fixture.nativeElement.querySelector('input');
expect(input.value).toBe('Blue');
}));
這個 "模型到檢視" 測試的執行步驟如下:
Here are the steps performed in the model to view test.
使用元件實例來設定
favoriteColor
的值。Use the component instance to set the value of the
favoriteColor
property.透過測試夾具(Fixture)來執行變更檢測。
Run change detection through the test fixture.
在
fakeAsync()
任務中使用tick()
方法來模擬時間的流逝。Use the
tick()
method to simulate the passage of time within thefakeAsync()
task.查詢表單輸入框元素的檢視。
Query the view for the form input element.
斷言輸入框的值與該元件實例的
favoriteColor
屬性值是匹配的。Assert that the input value matches the value of the
favoriteColor
property in the component instance.
後續步驟
Next steps
要進一步瞭解響應式表單,參閱下列章節:
To learn more about reactive forms, see the following guides:
要進一步瞭解範本驅動表單,參閱下列章節:
To learn more about template-driven forms, see the following guides:
建構範本驅動表單課程
Building a template-driven form tutorial
NgForm
指令 API 參考手冊NgForm
directive API reference