新聞中心
構建模板驅動表單
本教程將為你演示如何創(chuàng)建一個模板驅動表單,它的控件元素綁定到數(shù)據(jù)屬性,并通過輸入驗證來保持數(shù)據(jù)的完整性和樣式,以改善用戶體驗。

當在模板中進行更改時,模板驅動表單會使用雙向數(shù)據(jù)綁定來更新組件中的數(shù)據(jù)模型,反之亦然。
Angular 支持兩種交互式表單的設計方法。你可以使用 Angular 中的
模板語法和指令,以及本教程中描述的表單專用指令和技巧編寫模板來構建表單,或者你可以使用響應式方式(或叫模型驅動方式)來構建表單。
模板驅動表單適用于小型或簡單的表單,而響應式表單則更具伸縮性,適用于復雜表單。
你可以用 Angular 模板來構建各種表單,比如登錄表單、聯(lián)系人表單和幾乎所有的業(yè)務表單。你可以創(chuàng)造性地對控件進行布局并把它們綁定到對象模型的數(shù)據(jù)上。你可以指定驗證規(guī)則并顯示驗證錯誤,有條不紊地啟用或禁用特定控件,觸發(fā)內(nèi)置的視覺反饋等等。
本教程將向你展示如何通過一個簡化的范例表單來從頭構建一個表單,就像“英雄之旅”教程的中用一個表單來講解這些技巧一樣。
運行或下載范例應用:現(xiàn)場演練 / 下載范例。
目標
本教程將教你如何執(zhí)行以下操作:
- 使用組件和模板構建一個 Angular 表單
- 使用 ?
ngModel?創(chuàng)建雙向數(shù)據(jù)綁定,以便讀寫輸入控件的值 - 使用跟蹤控件狀態(tài)的特殊 CSS 類來提供視覺反饋
- 向用戶顯示驗證錯誤,并根據(jù)表單狀態(tài)啟用或禁用表單控件
- 使用模板引用變量在 HTML 元素之間共享信息
構建一個模板驅動表單
模板驅動表單依賴于 ?FormsModule ?定義的指令。
|
指令 |
詳細信息 |
|---|---|
NgModel | 會協(xié)調(diào)其附著在的表單元素中的值變更與數(shù)據(jù)模型中的變更,以便你通過輸入驗證和錯誤處理來響應用戶輸入。 |
NgForm | 會創(chuàng)建一個頂級的 |
NgModelGroup | 會創(chuàng)建 |
范例應用
英雄雇傭管理局使用本指南中的范例表單來維護英雄的個人信息。畢竟英雄也要工作啊。這個表單有助于該機構將正確的英雄與正確的危機匹配起來。
該表單突出了一些易于使用的設計特性。比如,這兩個必填字段的左邊是綠色條,以便讓它們醒目。這些字段都有初始值,所以表單是有效的,并且 Submit 按鈕也是啟用的。
當你使用這個表單時,你將學習如何包含驗證邏輯,如何使用標準 CSS 自定義表達式,以及如何處理錯誤條件以確保輸入的有效性。比如,如果用戶刪除了英雄的名字,那么表單就會失效。該應用會檢測已更改的狀態(tài),并以醒目的樣式顯示驗證錯誤。此外,Submit 按鈕會被禁用,輸入控件左側的“必填”欄也會從綠色變?yōu)榧t色。
步驟概述
在本教程中,你將使用以下步驟將一個范例表單綁定到數(shù)據(jù)并處理用戶輸入。
- 建立基本表單。
- 定義一個范例數(shù)據(jù)模型
- 包括必需的基礎設施,比如 ?
FormsModule? - 使用 ?
ngModel?指令和雙向數(shù)據(jù)綁定語法把表單控件綁定到數(shù)據(jù)屬性。 - 檢查 ?
ngModel?如何使用 CSS 類報告控件狀態(tài) - 為控件命名,以便讓 ?
ngModel?可以訪問它們 - 用 ?
ngModel?跟蹤輸入的有效性和控件的狀態(tài)。 - 添加自定義 CSS 來根據(jù)狀態(tài)提供可視化反饋
- 顯示和隱藏驗證錯誤信息
- 通過添加到模型數(shù)據(jù)來響應原生 HTML 按鈕的單擊事件
- 使用表單的 ?
ngSubmit?輸出屬性來處理表單提交。 - 在表單生效之前,先禁用 Submit 按鈕
- 在提交完成后,把已完成的表單替換成頁面上不同的內(nèi)容
建立表單
你可以根據(jù)這里提供的代碼從頭創(chuàng)建范例應用,也可以查看 現(xiàn)場演練 / 下載范例。
- 這里提供的范例應用會創(chuàng)建一個 ?
Hero?類,用于定義表單中所反映的數(shù)據(jù)模型。 - 該表單的布局和細節(jié)是在 ?
HeroFormComponent? 類中定義的。 - 下面的代碼會創(chuàng)建一個新的 hero 實例,以便讓初始的表單顯示一個范例英雄。
- 該應用啟用了表單功能,并注冊了已創(chuàng)建的表單組件。
- 該表單顯示在根組件模板定義的應用布局中。
- Name ?
? 控件元素中包含了 HTML5 的 ?required?屬性 - Alter Ego ?
? 沒有控件元素,因為 ?alterEgo?是可選的 - 范例表單使用的是 Twitter Bootstrap 中的一些樣式類:?
container?,?form-group?,?form-control? 和 ?btn?。要使用這些樣式,就要在該應用的樣式表中導入該庫。 - 這份表單讓英雄申請人從管理局批準過的固定清單中選出一項超能力。預定義 ?
powers?列表是數(shù)據(jù)模型的一部分,在 ?HeroFormComponent?內(nèi)部維護。Angular 的?NgForOf?指令會遍歷這些數(shù)據(jù)值,以填充這個 ?? 元素。
export class Hero {
constructor(
public id: number,
public name: string,
public power: string,
public alterEgo?: string
) { }
}
import { Component } from '@angular/core';
import { Hero } from '../hero';
@Component({
selector: 'app-hero-form',
templateUrl: './hero-form.component.html',
styleUrls: ['./hero-form.component.css']
})
export class HeroFormComponent {
powers = ['Really Smart', 'Super Flexible',
'Super Hot', 'Weather Changer'];
model = new Hero(18, 'Dr IQ', this.powers[0], 'Chuck Overstreet');
submitted = false;
onSubmit() { this.submitted = true; }
}該組件的 ?selector ?值為 “app-hero-form”,意味著你可以用 ?? 標簽把這個表單放到父模板中。
const myHero = new Hero(42, 'SkyDog',
'Fetch any object at any distance',
'Leslie Rollover');
console.log('My hero is called ' + myHero.name); // "My hero is called SkyDog"這個演示使用虛擬數(shù)據(jù)來表達 ?model ?和 ?powers?。在真正的應用中,你會注入一個數(shù)據(jù)服務來獲取和保存實際數(shù)據(jù),或者把它們作為輸入屬性和輸出屬性進行公開。
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { AppComponent } from './app.component';
import { HeroFormComponent } from './hero-form/hero-form.component';
@NgModule({
imports: [
BrowserModule,
CommonModule,
FormsModule
],
declarations: [
AppComponent,
HeroFormComponent
],
providers: [],
bootstrap: [ AppComponent ]
})
export class AppModule { }
初始模板定義了一個帶有兩個表單組和一個提交按鈕的表單布局。表單組對應于 Hero 數(shù)據(jù)模型的兩個屬性:name 和 alterEgo。每個組都有一個標簽和一個用戶輸入框。
Submit 按鈕里面有一些用于樣式化的類。此時,表單布局全都是純 HTML5,沒有綁定或指令。
@import url('https://unpkg.com/bootstrap@3.3.7/dist/css/bootstrap.min.css');
如果你現(xiàn)在正在運行該應用,你會看到選擇控件中的超能力列表。由于尚未將這些 input 元素綁定到數(shù)據(jù)值或事件,因此它們?nèi)匀皇强瞻椎?,沒有任何行為。
把輸入控件綁定到數(shù)據(jù)屬性
下一步是使用雙向數(shù)據(jù)綁定把輸入控件綁定到相應的 ?Hero ?屬性,這樣它們就可以通過更新數(shù)據(jù)模型來響應用戶的輸入,并通過更新顯示來響應數(shù)據(jù)中的程序化變更。
該 ?ngModel ?指令是由 ?FormsModule ?聲明的,它能讓你把模板驅動表單中的控件綁定到數(shù)據(jù)模型中的屬性。當你使用雙向數(shù)據(jù)綁定的語法 ?[(ngModel)]? 引入該指令時,Angular 就可以跟蹤控件的值和用戶交互,并保持視圖與模型的同步。
- 編輯模板 ?
hero-form.component.html?。 - 找到 Name 標簽旁邊的 ?
? 標記。 - 使用雙向數(shù)據(jù)綁定語法 ?
[(ngModel)]="..."? 添加 ?ngModel?指令。
TODO: remove this: {{model.name}}
這個例子中在每個 input 標記后面都有一個臨時的診斷插值 ?
{{model.name}}?,以顯示相應屬性的當前數(shù)據(jù)值。本提醒是為了讓你在觀察完這個雙向數(shù)據(jù)綁定后刪除這些診斷行。
訪問表單的整體狀態(tài)
當你導入了 ?FormsModule ?時,Angular 會自動為模板中的 ?? 標簽創(chuàng)建并附加一個 ?NgForm ?指令。(因為 ?NgForm ?定義了一個能匹配 ?? 元素的選擇器 ?form?)。
要訪問 ?NgForm ?和表單的整體狀態(tài),就要聲明一個模板引用變量。
- 編輯模板 ?
hero-form.component.html?。 - 為 ?
? 標簽添加模板引用變量 ?#heroForm?,并把它的值設置如下。 - 運行該應用。
- 開始在 Name 輸入框中輸入。
模板變量 ?heroForm ?現(xiàn)在是對 ?NgForm ?指令實例的引用,該指令實例管理整個表單。
在添加和刪除字符時,你可以看到它們從數(shù)據(jù)模型中出現(xiàn)和消失。比如:
用來顯示插值的診斷行證明了這些值確實從輸入框流向了模型,然后再返回。
為控件元素命名
在元素上使用 ?[(ngModel)]? 時,必須為該元素定義一個 ?name ?屬性。Angular 會用這個指定的名字來把這個元素注冊到父 ?? 元素上的 ?NgForm ?指令中。
這個例子中為 ?? 元素添加了一個 ?name ?屬性,并把它的值設置為 “name”,用來表示英雄的名字。任何唯一的值都可以用,但最好用描述性的名稱。
- 為Alter Ego和Hero Power添加類似的 ?
[(ngModel)]? 綁定和 ?name?屬性。 - 你現(xiàn)在可以移除顯示插值的診斷消息了。
- 要想確認雙向數(shù)據(jù)綁定是否在整個英雄模型上都有效,可以在該組件的頂部添加一個帶有 ?
json?管道的新文本綁定。?json?管道會把數(shù)據(jù)序列化為字符串。 - 注意,每個 ?
? 元素都有一個 ?id?屬性。?? 元素的 ?for?屬性用它來把標簽匹配到輸入控件。這是一個標準的 HTML 特性。 - 每個 ?
? 元素都有一個必需的 ?name?屬性,Angular 用它來注冊表單中的控件。 - 你已經(jīng)觀察到了這種效果,可以刪除 ?
{{ model | json }}? 的文本綁定了。
表單模板修改完畢后,應如下所示:
{{ model | json }}
如果你現(xiàn)在運行該應用并更改英雄模型的每個屬性,該表單可能會顯示如下:
通過表單頂部的診斷行可以確認所有的更改都已反映在模型中。
跟蹤控件狀態(tài)
控件上的 ?NgModel ?指令會跟蹤該控件的狀態(tài)。它會告訴你用戶是否接觸過該控件、該值是否發(fā)生了變化,或者該值是否無效。Angular 在控件元素上設置了特殊的 CSS 類來反映其狀態(tài),如下表所示。
|
狀態(tài) |
為 TRUE 時的類名 |
為 FALSE 時的類名 |
|---|---|---|
該控件已被訪問過。 | ng-touched | ng-untouched |
控件的值已被更改。 | ng-dirty | ng-pristine |
控件的值是有效的。 | ng-valid | ng-invalid |
此外,Angular 還會在提交時把 ?ng-submitted? 類應用到 ?? 元素上。這個類不會應用到內(nèi)部控件上。
你可以用這些 CSS 類來根據(jù)控件的狀態(tài)定義其樣式。
觀察控件狀態(tài)
要想知道框架是如何添加和移除這些類的,請打開瀏覽器的開發(fā)者工具,檢查代表英雄名字的 ??
- 使用瀏覽器的開發(fā)者工具,找到與 “Name” 輸入框對應的 ?
? 元素。除了 “form-control” 類之外,你還可以看到該元素有多個 CSS 類。 - 當你第一次啟動它的時候,這些類表明它是一個有效的值,該值在初始化或重置之后還沒有改變過,并且在該控件自初始化或重置后也沒有被訪問過。
- 在 Name ?
? 框中執(zhí)行以下操作,看看會出現(xiàn)哪些類。 - 查看,但不要碰它。這些類表明它沒有被碰過、還是最初的值,并且有效。
- 在 Name 框內(nèi)單擊,然后單擊它外部。該控件現(xiàn)在已被訪問過,該元素具有 ?
ng-touched? 類,取代了 ?ng-untouched? 類。 - 在名字的末尾添加斜杠?,F(xiàn)在它被碰過,而且是臟的(變化過)。
- 刪掉這個名字。這會使該值無效,所以 ?
ng-invalid? 類會取代 ?ng-valid? 類。
為狀態(tài)創(chuàng)建視覺反饋
注意 ?ng-valid? / ?ng-invalid? 這兩個類,因為你想在值無效時發(fā)出強烈的視覺信號。你還要標記必填字段。
你可以在輸入框的左側用彩條標記必填字段和無效數(shù)據(jù):
要想用這種方式修改外觀,請執(zhí)行以下步驟。
- 為 ?
ng-*? CSS 類添加一些定義。 - 把這些類定義添加到一個新的 ?
forms.css? 文件中。 - 把這個新文件添加到項目中,作為 ?
index.html? 的兄弟: - 在 ?
index.html? 文件中,更新 ?? 標簽以包含新的樣式表。
.ng-valid[required], .ng-valid.required {
border-left: 5px solid #42A948; /* green */
}
.ng-invalid:not(form) {
border-left: 5px solid #a94442; /* red */
}
顯示和隱藏驗證錯誤信息
Name 輸入框是必填的,清除它就會把彩條變成紅色。這表明有些東西是錯的,但是用戶并不知道要怎么做或該做什么。你可以通過查看和響應控件的狀態(tài)來提供有用的信息。
當用戶刪除該名字時,該表單應如下所示:
Hero Power 選擇框也是必填的,但它不需要這樣的錯誤處理,因為選擇框已經(jīng)把選擇限制在有效值范圍內(nèi)。
要在適當?shù)臅r候定義和顯示錯誤信息,請執(zhí)行以下步驟。
- 使用模板引用變量擴展 ?
? 標簽,你可以用來從模板中訪問輸入框的 Angular 控件。在這個例子中,該變量是 ?#name="ngModel"?。 - 添加一個包含合適錯誤信息 ??
- 通過把 ?
name?控件的屬性綁定到 ?? 元素的 ?hidden?屬性來顯示或隱藏錯誤信息。- 為 ?
name?輸入框添加一個有條件的錯誤信息,如下例所示。Name is required關于 "PRISTINE"(原始)狀態(tài)的說明
在這個例子中,當控件是有效的(valid)或者是
原始的(pristine)時,你會隱藏這些消息。原始表示該用戶在此表單中顯示的值尚未更改過。如果你忽略了 ?
pristine?狀態(tài),那么只有當值有效時才會隱藏這些消息。如果你把一個新的(空白)英雄或一個無效的英雄傳給這個組件,你會立刻看到錯誤信息,而這時候你還沒有做過任何事情。
你可能希望只有在用戶做出無效更改時,才顯示該消息。因此當 ?
pristine?狀態(tài)時,隱藏這條消息就可以滿足這個目標。當你在下一步中為表單添加一個新的英雄時,就會看到這個選擇有多重要。添加一個新英雄
本練習通過添加模型數(shù)據(jù),展示了如何響應原生 HTML 按鈕單擊事件。要讓表單用戶添加一個新的英雄,就要添加一個能響應 click 事件的 New Hero 按鈕。
- 在模板中,把 “New Hero” 這個 ?
? 元素放在表單底部。 - 在組件文件中,把創(chuàng)建英雄的方法添加到英雄數(shù)據(jù)模型中。
- 把按鈕的 click 事件綁定到一個創(chuàng)建英雄的方法 ?
newHero()? 上。 - 再次運行該應用,單擊 New Hero 按鈕。
- 輸入一個名字,然后再次點擊 New Hero。
- 要恢復表單控件的原始狀態(tài),可以在調(diào)用 ?
newHero()? 方法之后強制調(diào)用表單的 ?reset()? 方法以清除所有標志。
newHero() { this.model = new Hero(42, '', ''); }表單會清空,輸入框左側的必填欄會顯示紅色,說明 ?
name?和 ?power?屬性無效。請注意,錯誤消息是隱藏的。這是因為表單處于原始狀態(tài)。你還沒有改過任何東西。現(xiàn)在,該應用會顯示一條錯誤信息 ?
Name is required?,因為該輸入框不再是原始狀態(tài)。表單會記住你在單擊 New Hero 之前輸入過一個名字。現(xiàn)在單擊 New Hero 會重置表單及其控件標志。
使用 ngSubmit 提交表單
用戶應該可以在填寫之后提交這個表單。表單底部的 Submit 按鈕本身沒有任何作用,但由于它的類型(?
type="submit"?),它會觸發(fā)一個表單提交事件。要響應此事件,請執(zhí)行以下步驟。- 把表單的 ?
ngSubmit?事件屬性綁定到一個 hero-form 組件的 ?onSubmit()? 方法中。 - 使用模板引用變量 ?
#heroForm? 訪問包含 Submit 按鈕的表單,并創(chuàng)建一個事件綁定。你可以把表示它整體有效性的 form 屬性綁定到 Submit 按鈕的 ?disabled?屬性上。 - 運行該應用。注意,該按鈕已啟用 - 雖然它還沒有做任何有用的事情。
- 刪除名稱值。這違反了“必需”規(guī)則,因此會顯示錯誤消息,并注意它還會禁用“提交”按鈕。
你不必把按鈕的啟用狀態(tài)明確地關聯(lián)表單的有效性上。當 ?
FormsModule?在增強的表單元素上定義模板引用變量時,會自動執(zhí)行此操作,然后在按鈕控件中引用該變量。響應表單提交
要展示對表單提交的響應,你可以隱藏數(shù)據(jù)輸入?yún)^(qū)域并就地顯示其它內(nèi)容。
- 把整個表單包裹進一個 ?? 中并把它的 ?
hidden?屬性綁定到 ?HeroFormComponent.submitted? 屬性上。Hero Form
- 主表單從一開始就是可見的,因為在提交之前,它的 ?
submitted?屬性都是 false,正如 ?HeroFormComponent?中的這個片段所顯示的:
submitted = false; onSubmit() { this.submitted = true; }- 點擊 Submit 按鈕后,?
submitted?標志就變?yōu)?nbsp;?true?,表單就會消失。- 要在表單處于已提交狀態(tài)時顯示其它內(nèi)容,請在新的 ?
? 包裝器下添加以下 HTML。You submitted the following:
Name{{ model.name }}Alter Ego{{ model.alterEgo }}Power{{ model.power }}
這個 ?
?(用于顯示帶插值綁定的只讀英雄)只在組件處于已提交狀態(tài)時才會出現(xiàn)。另外還顯示了一個 Edit 按鈕,它的 click 事件綁定到了一個清除 ?
submitted?標志的表達式。- 單擊 Edit 按鈕,將顯示切換回可編輯的表單。
總結
本頁討論的 Angular 表單利用了下列框架特性來支持數(shù)據(jù)修改,驗證等工作。
- 一個 Angular HTML 表單模板
- 帶 ?
@Component? 裝飾器的表單組件類 - 綁定到 ?
NgForm.ngSubmit? 事件屬性來處理表單提交 - 模板引用變量,比如 ?
#heroForm? 和 ?#name? - 雙向數(shù)據(jù)綁定的 ?
[(ngModel)]? 語法 - ?
name?屬性的用途是驗證和表單元素的變更跟蹤 - 用輸入控件上的引用變量的 ?
valid? 屬性來檢查控件是否有效,并據(jù)此顯示或隱藏錯誤信息 - 用 ?
NgForm?的有效性來控制 Submit 按鈕的啟用狀態(tài) - 自定義 CSS 類,為用戶提供關于無效控件的視覺反饋
這里是該應用最終版本的代碼:
- hero-form/hero-form.component.ts
import { Component } from '@angular/core'; import { Hero } from '../hero'; @Component({ selector: 'app-hero-form', templateUrl: './hero-form.component.html', styleUrls: ['./hero-form.component.css'] }) export class HeroFormComponent { powers = ['Really Smart', 'Super Flexible', 'Super Hot', 'Weather Changer']; model = new Hero(18, 'Dr IQ', this.powers[0], 'Chuck Overstreet'); submitted = false; onSubmit() { this.submitted = true; } newHero() { this.model = new Hero(42, '', ''); } }- hero-form/hero-form.component.html
Hero Form
You submitted the following:
Name{{ model.name }}Alter Ego{{ model.alterEgo }}Power{{ model.power }}
- hero.ts
export class Hero { constructor( public id: number, public name: string, public power: string, public alterEgo?: string ) { } }- app.module.ts
import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { CommonModule } from '@angular/common'; import { FormsModule } from '@angular/forms'; import { AppComponent } from './app.component'; import { HeroFormComponent } from './hero-form/hero-form.component'; @NgModule({ imports: [ BrowserModule, CommonModule, FormsModule ], declarations: [ AppComponent, HeroFormComponent ], providers: [], bootstrap: [ AppComponent ] }) export class AppModule { }- app.component.html
- app.component.ts
import { Component } from '@angular/core'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent { }- main.ts
import { enableProdMode } from '@angular/core'; import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; import { AppModule } from './app/app.module'; import { environment } from './environments/environment'; if (environment.production) { enableProdMode(); } platformBrowserDynamic().bootstrapModule(AppModule);- forms.css
.ng-valid[required], .ng-valid.required { border-left: 5px solid #42A948; /* green */ } .ng-invalid:not(form) { border-left: 5px solid #a94442; /* red */ }
網(wǎng)站標題:創(chuàng)新互聯(lián)Angular教程:Angular構建模板驅動表單
瀏覽路徑:http://m.5511xx.com/article/dhecpho.html - 主表單從一開始就是可見的,因為在提交之前,它的 ?
- 通過把 ?
模板引用變量(?
#name?)設置為 ?"ngModel"?,因為 "ngModel" 是 ?NgModel.exportAs? 屬性的值。這個屬性告訴 Angular 如何把引用變量和指令鏈接起來。


咨詢
建站咨詢
