溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Angular2如何實(shí)現(xiàn)模板驅(qū)動的表單

發(fā)布時(shí)間:2021-08-17 14:41:57 來源:億速云 閱讀:151 作者:小新 欄目:web開發(fā)

這篇文章給大家分享的是有關(guān)Angular2如何實(shí)現(xiàn)模板驅(qū)動的表單的內(nèi)容。小編覺得挺實(shí)用的,因此分享給大家做個(gè)參考,一起跟隨小編過來看看吧。

在網(wǎng)頁開發(fā)中,表單估計(jì)是最常用的一個(gè),同時(shí)也是最麻煩、最容易出問題的。在一個(gè)稍微復(fù)雜一點(diǎn)的應(yīng)用中,我們除了用表單元素收集數(shù)據(jù),還需要驗(yàn)證,幾個(gè)數(shù)據(jù)之間可能還會相互關(guān)聯(lián),然后根據(jù)不同的數(shù)據(jù)值調(diào)用不同的業(yè)務(wù)邏輯等。

使用Angular提供的數(shù)據(jù)綁定的功能,我們可以很容易就在組件中獲得用戶輸入的數(shù)據(jù),Angular也提供了幾種驗(yàn)證方式方便我們進(jìn)行數(shù)據(jù)的校驗(yàn)。但是,一些自定義的數(shù)據(jù)驗(yàn)證、數(shù)據(jù)交互和業(yè)務(wù)邏輯還是需要自己處理。

在Angular2中,提供了2種表單實(shí)現(xiàn)方式,分別是'template-driven'(模板驅(qū)動的表單)和'model-driven'(模型驅(qū)動表單)。在這篇文章中,我們先來看看模板驅(qū)動的表單。顧名思義,模板驅(qū)動的表單就是大部分表單相關(guān)代碼都在模板里,通過在模板里面添加ngForm, ngModel和ngModelGroup等屬性來定義模板和驗(yàn)證信息,以及它跟組件之間的數(shù)據(jù)交互。

實(shí)例

下圖是這篇文章使用的實(shí)例的界面:

Angular2如何實(shí)現(xiàn)模板驅(qū)動的表單

它是一個(gè)用戶信息輸入的表單,包括4個(gè)字段,用戶名、電話、城市和街道,演示了如何使用表單,給各個(gè)字段添加驗(yàn)證并顯示驗(yàn)證結(jié)果,以及如何在組件中判斷是否出錯(cuò)并獲取出錯(cuò)信息。

項(xiàng)目源碼可以從github獲取,這個(gè)項(xiàng)目包含了幾個(gè)Angular2表單相關(guān)的實(shí)例,可以使用下面的命令獲取本文所對應(yīng)的代碼:

git clone https://github.com/Mavlarn/angular2-forms-tutorial

然后進(jìn)入項(xiàng)目目錄,運(yùn)行下面的命令安裝依賴然后運(yùn)行測試服務(wù)器

cd angular2-forms-tutorial
git checkout template-driven # 檢出該文所使用的tag
npm install
npm start

該項(xiàng)目是基于之前的Angular2-basic模板,這個(gè)教程相關(guān)的代碼都在'template-forms'目錄里面。

引入FormsModule

首先,我們需要在app.module.ts里引入FormsModule。

import { FormsModule } from '@angular/forms';
//省略其他
@NgModule({
 imports: [ BrowserModule, FormsModule ],
 //省略其他
})

初始表單

然后,我們從一個(gè)基本的html表單開始:

<form>
 <label>姓名:</label>
 <input type="text">
 <label>電話:</label>
 <input type="text">
 <label>城市:</label>
 <input type="text">
 <label>街道:</label>
 <input type="text">
 <button type="submit">保存</button>
</form>

在實(shí)際的實(shí)例中,使用了bootstrap的表單樣式,一組輸入框應(yīng)該是下面這個(gè)樣子,但是在本文中,為了節(jié)省頁面顯示的篇幅,我省略了div, form-group等,我們只需要關(guān)心如何在Angular2中使用模板驅(qū)動的表單。如果想查看完整的帶樣式的代碼,請查看源文件。

<div class="form-group">
  <label class="col-sm-2 control-label">姓名:</label>
  <div class="col-sm-10">
    <input class="form-control" type="text">
  </div>
</div>

ngForm

在上面的表單里,我們沒有使用Angular2的任何功能,如數(shù)據(jù)綁定,也沒有使用其他指令。但是,Angular2在<form>上實(shí)現(xiàn)了一個(gè)指令'ngForm',這樣,對于所有的html的form表單,都會使用ngForm組件去初始化該表單。

使用ngForm對象

接下來,我們需要在模板里面訪問這個(gè)ngForm的實(shí)例,這樣我們就能夠從這個(gè)實(shí)例里面獲取數(shù)據(jù),或者獲取數(shù)據(jù)驗(yàn)證狀態(tài)。
在Angular2里,都提供了一個(gè)模板引用變量的功能,通過#加變量實(shí)現(xiàn)。通過這個(gè)功能,我們可以在同一元素、兄弟元素或任何子元素中引用模板引用變量。這樣聽著還是不好理解,我們看一個(gè)例子:

<input #phone placeholder="phone number">
<button (click)="callPhone(phone.value)">Call</button>

在這個(gè)例子中,我們通過#phone定義了一個(gè)變量,它所指的就是這個(gè)input元素,phone.value也就是這個(gè)輸入框輸入的值。

除了使用#,也可以使用ref-,例如ref-phone形式的定義跟#phone是一樣的。

我們可以對任何的DOM元素使用這種方式獲取當(dāng)前引用,也可以對任何的Angular2的指令使用。在這個(gè)表單的例子中,我們這樣來獲取這個(gè)ngFrom的引用:

<form #userForm="ngForm">

其中'ngForm'就是當(dāng)前這個(gè)指令,這樣在這個(gè)模板里面,我們可以用userForm獲得表單的所有數(shù)據(jù)。

提交表單

在html中,我們要提交一個(gè)form,會在form里寫一個(gè)action的屬性,然后,用一個(gè)類型為'submit'的按鈕來提交。但是,在Angular2中,我們需要使用ngSubmit事件:

<form #userForm="ngForm" (ngSubmit)="logForm(userForm)">
  <button type="submit">保存</button>
</form>

這樣,當(dāng)用戶點(diǎn)擊保存按鈕的時(shí)候,Angular2會使用自己的驗(yàn)證機(jī)制,驗(yàn)證所有的數(shù)據(jù),然后在調(diào)用'logForm(userForm)'方法。

在我們的組件中,實(shí)現(xiàn)這個(gè)方法:

logForm(theForm: NgForm) {
  console.log(theForm.value);
  if (theForm.invalid) {
    // handle error.
  }
}

在這個(gè)方法里,我們使用theForm.invalid就可以獲得這個(gè)表單是否驗(yàn)證成功的狀態(tài),也可以用'theForm.value'獲得所有的表單數(shù)據(jù)。在這里,我們把表單數(shù)據(jù)打印到控制臺來檢查數(shù)據(jù)。至于如何從這個(gè)表單引用中獲取控件數(shù)據(jù)和狀態(tài),會在接下來再講。

使用ngModel綁定數(shù)據(jù)

接下來,我們需要綁定數(shù)據(jù)。假設(shè)我們的業(yè)務(wù)是打開這個(gè)頁面的時(shí)候獲取用戶數(shù)據(jù),然后顯示到頁面表單上。我們在組件的構(gòu)造方法中創(chuàng)建一個(gè)模擬的用戶數(shù)據(jù):

export class TemplateFormsComponent {
  user: any;
  constructor() {
    this.user = {
      name: '張三',
      mobile: 13800138001,
      city: '北京',
      street: '朝陽望京...'
    };
  }
}

然后在模板中將這個(gè)組件中的數(shù)據(jù)綁定到模板頁面上:

<input type="text" name="name" [(ngModel)]="user.name">
<input type="text" name="mobile" [ngModel]="user.mobile">
<input type="text" name="city" [ngModel]="user.city">
<input type="text" name="street" [ngModel]="user.street">
<!-- 其他的輸入框都類似 -->

在這里,我們使用[(ngModel)]="user.name",這是雙向綁定的方式,這樣,當(dāng)我們修改頁面上的數(shù)據(jù)的時(shí)候,在組件中也能獲得更新后的數(shù)據(jù);同時(shí),如果在組件中更新了數(shù)據(jù),在頁面上也能更新。

為了演示這個(gè)雙向綁定跟單向綁定的區(qū)別,我們只對姓名使用雙向綁定,對其他的都是用單向綁定,也就是[ngModel]="user.mobile"。使用[]的單向綁定是從模板到組件的綁定,也就是頁面中的輸入的數(shù)據(jù)改變,組件中的數(shù)據(jù)也會改變。但是組件中的數(shù)據(jù)更新不會引起頁面上該數(shù)據(jù)的更新。

使用單向綁定可以減少數(shù)據(jù)的更新檢查,從來可以提高性能。

如果不需要數(shù)據(jù)的初始化,我們其實(shí)可以只用ngModel,例如:

<input type="text" name="city" ngModel>

這樣,我們在組件中創(chuàng)建的用戶數(shù)據(jù)就無法顯示到頁面上,但是,他還是能夠?qū)㈨撁嫔陷斎氲臄?shù)據(jù)綁定到組件中的數(shù)據(jù)上。

在Angular2中,使用ngModel結(jié)合name屬性來創(chuàng)建一個(gè)表單控件FormControls。例如上面的<input name="city" ngModel>就對應(yīng)一個(gè)userForm里面的控件city。由于我們在提交方法里面將這個(gè)userForm作為參數(shù)傳到方法里,我們可以在方法里面獲得所有的表單控件theForm.controls,它是一個(gè)Map類型的對象,key是所有的表單元素的name,值就是一個(gè)FormControl對象,里面保存著數(shù)據(jù)、和驗(yàn)證結(jié)果、是否修改等狀態(tài)。也正是因?yàn)檫@些FormControls,我們才能夠使用theForm.value的方式獲取表單里的數(shù)據(jù)。當(dāng)我們點(diǎn)擊保存按鈕的時(shí)候,就能在日志里面看到表單的數(shù)據(jù):

{
  name: "張三",
  mobile: 13800138001,
  city: "北京",
  street: "朝陽望京..."
}

使用ngModelGroup分組顯示

一般情況下,我們的model數(shù)據(jù)有可能是嵌套的,比如對于用戶信息來說,城市和街道可能在一個(gè)地址對象address里,例如:

{
  name: "張三",
  mobile: 13800138001,
  address: {
    city: "北京",
    street: "朝陽望京..."
  }
}

對于這樣的數(shù)據(jù),我們就可以使用ngModelGroup來分組。模板就是這樣:

<form #userForm="ngForm" (ngSubmit)="logForm(userForm)">
 <label>姓名:</label>
 <input type="text" name="name" [(ngModel)]="user.name">
 <label>電話:</label>
 <input type="text" name="mobile" [ngModel]="user.mobile">
 <fieldset ngModelGroup="address">
  <label>城市:</label>
  <input type="text" name="city" [ngModel]="user.address.city">
  <label>街道:</label>
  <input type="text" name="street" [ngModel]="user.address.street">
  <button type="submit">保存</button>
 </fieldset>
</form>

這樣我們就把地址信息都封裝到一個(gè)address對象里面。注意我們綁定的數(shù)據(jù)的結(jié)構(gòu)也發(fā)生改變,這樣,我們也需要修改我們的組件里面的用戶數(shù)據(jù):

export class TemplateFormsComponent {
  user: any;
  constructor() {
    this.user = {
      name: '張三',
      mobile: 13800138001,
      address: {
        city: '北京',
        street: '朝陽望京...'
      }
    };
  }
}

至此,我們的表單的基本功能就算完成了。我們在面板中創(chuàng)建了表單,在組件中初始化了用戶數(shù)據(jù),并顯示到頁面上,在頁面上用ngModel,將頁面上的數(shù)據(jù)更改綁定到組件上。同時(shí),使用name屬性,使得表單里面的所有數(shù)據(jù)都成為FormControl對象。在提交所調(diào)用的方法里,獲得了表單的驗(yàn)證狀態(tài)和數(shù)據(jù)。

表單控件的驗(yàn)證和狀態(tài)

下一步,我們來添加數(shù)據(jù)驗(yàn)證,Angular2為我們提供了幾種最基本的驗(yàn)證:

  • required:表明該數(shù)據(jù)是必須的。

  • minlength:設(shè)置該字段的長度的最小值,即使輸入的是數(shù)字,也按照字符串來判斷長度。

  • maxlength:設(shè)置該字段的長度的最大值。

  • pattern:使用正則表達(dá)式驗(yàn)證

在使用Angular的驗(yàn)證之前,我們首先需要關(guān)閉瀏覽器默認(rèn)的驗(yàn)證,不然,如果某一個(gè)輸入不合法,提交按鈕就無法提交。我們在form里添加novalidate:

<form #userForm="ngForm" (ngSubmit)="logForm(userForm)" novalidate>

然后,我們對姓名輸入框添加驗(yàn)證,并根據(jù)驗(yàn)證的結(jié)果顯示不同的提示,同時(shí),為了演示Angular2表單控件的特性,再添加幾個(gè)提示,來顯示該值的狀態(tài),代碼如下:

<input type="text" name="name" [(ngModel)]="user.name" #name="ngModel" required minlength="3">
<span *ngIf="name.pristine" class="label label-primary">未修改</span>
<span *ngIf="name.dirty" class="label label-warning">已修改</span>
<span *ngIf="name.valid" class="label label-success">有效</span>
<div [hidden]="name.valid || name.pristine" class="alert alert-danger">
  <p *ngIf="name.errors?.minlength">姓名最小長度為3</p>
  <p *ngIf="name.errors?.required">必須輸入姓名</p>
</div>

首先,我們在input上添加了2個(gè)驗(yàn)證,required和minlength="3"。

其次,我們使用#name="ngModel"創(chuàng)建了一個(gè)模板引用變量,這樣我們在下面就可以使用name來獲取這個(gè)表單控件(FormControl)的引用。表單控件有一些屬性,如pristine, dirty, valid, touched,這幾個(gè)都是狀態(tài)類型,表示某一種狀態(tài)是否為真。除此以外還有控件的值可以用name.value獲取。最后,還有驗(yàn)證的錯(cuò)誤信息結(jié)果,會放在name.errors里。

在上面的代碼里,我們用<span *ngIf="name.pristine" class="label label-primary">未修改</span>,在控件值未被修改的時(shí)候,顯示一個(gè)lebel。同樣,在被修改、驗(yàn)證有效的時(shí)候顯示相應(yīng)的標(biāo)簽。

最后,所有的驗(yàn)證結(jié)果的錯(cuò)誤信息會保存在name.errors里,如果沒有數(shù)據(jù)驗(yàn)證錯(cuò)誤,這個(gè)errors值就是null,所以,在上面的代碼里,我們用name.errors?.minlength,這表示,如果errors不為null,而且errors.minlength也不為空的時(shí)候,才顯示里面的信息。

我們可以看到,表單控件的驗(yàn)證會將驗(yàn)證器的名字作為key放在errors里面,對應(yīng)的值是true。我們就是用這個(gè)特性,來根據(jù)控件驗(yàn)證的不同結(jié)果,來顯示友好的錯(cuò)誤信息。

如果運(yùn)行我們的實(shí)例,可以發(fā)現(xiàn),對于姓名,如果清空它的值,發(fā)現(xiàn)只有一個(gè)錯(cuò)誤信息,就是必須輸入姓名。你可能會覺得,這時(shí)候,值為空,那他的長度也小于3,那么minlength這個(gè)錯(cuò)誤也應(yīng)該被檢測到才對,但是實(shí)際上,遇到第一個(gè)錯(cuò)誤以后,就沒有其他的驗(yàn)證。

在上面姓名輸入框上,我們使用#name="ngModel"創(chuàng)建了一個(gè)模板引用變量,然后在接下來的模板里面使用它獲得表單控件。實(shí)際上,我們也可以直接使用之前定義的對ngForm的引用,來獲得這個(gè)表單里所有控件的狀態(tài)。例如,對電話,我們使用下面的方式:

<input type="text" name="mobile" [ngModel]="user.mobile" required minlength="11" maxlength="11">
<span *ngIf="userForm.controls.mobile?.pristine" class="label label-primary">未修改</span>
<span *ngIf="userForm.controls.mobile?.dirty" class="label label-warning">已修改</span>
<span *ngIf="userForm.controls.mobile?.valid" class="label label-success">有效</span>
<div [hidden]="userForm.controls.mobile?.valid || userForm.controls.mobile?.pristine" class="alert alert-danger">
  <p *ngIf="userForm.controls.mobile?.errors?.minlength">電話長度必須為11</p>
  <p *ngIf="userForm.controls.mobile?.errors?.required">必須輸入電話</p>
</div>

在這里,我們沒有獲取對mobile的模板引用,而是用ngForm的引用獲得:

userForm.controls.mobile?.pristine

當(dāng)獲取驗(yàn)證錯(cuò)誤結(jié)果時(shí):

userForm.controls.mobile?.errors?.minlength

注意這里在mobile上就使用?是因?yàn)?,在使用ngIf渲染頁面上的元素的時(shí)候,這個(gè)表單控件還沒有初始化完成,如果不加這個(gè)?,就會出現(xiàn)錯(cuò)誤。

根據(jù)驗(yàn)證狀態(tài)定義樣式

Angular的表單驗(yàn)證,除了在控件上的數(shù)據(jù)以外,它還會根據(jù)狀態(tài)在控件所在的html元素上添加css樣式:

Angular2如何實(shí)現(xiàn)模板驅(qū)動的表單

所以,我們只需要定義相關(guān)的css,就可以實(shí)現(xiàn)根據(jù)狀態(tài)顯示不同的效果。

.ng-valid[required], .ng-valid.required {
 border-left: 5px solid #42A948; /* green */
}
.ng-invalid:not(form).ng-invalid:not(fieldset) {
 border-left: 5px solid #a94442; /* red */
}

結(jié)合各種css的選擇器,我們就可以根據(jù)表單控件的狀態(tài)實(shí)現(xiàn)各種顯示的樣式。

在組件中獲取表單控件數(shù)據(jù)

最后,我們再看看怎樣在組件中獲取這些控件的狀態(tài)和結(jié)果,在上面,我們給ngForm添加了一個(gè)提交方法:

<form #userForm="ngForm" (ngSubmit)="logForm(userForm)" novalidate>

然后在組件中,這個(gè)logForm(userForm)方法如下:

logForm(theForm: NgForm) {
  if (theForm.invalid) {
    if (theForm.controls['name'].errors) {
      this.nameErrorMsg = 'name error:' + JSON.stringify(theForm.controls['name'].errors);
    } else {
      this.nameErrorMsg = null;
    }
  }
  console.log(theForm.value);
}

在這個(gè)方法里,theForm就是ngForm的模板引用實(shí)例,類型是NgForm的。

如果表單驗(yàn)證有失敗,theForm.invalid就是false。

theForm.controls就是這個(gè)表單里的所有控件,如果想獲取姓名的驗(yàn)證結(jié)果,就是theForm.controls['name'].errors。

用這種方式,我們就可以在組件中獲取所有表單控件的數(shù)據(jù)、驗(yàn)證狀態(tài)、錯(cuò)誤信息等。

重置表單

一般情況下,如果是新建用戶信息,我們需要在保存成功以后,清空當(dāng)前數(shù)據(jù),重置表單的狀態(tài),等待用戶重新輸入。如果我們只是清空數(shù)據(jù),這時(shí)候那些驗(yàn)證錯(cuò)誤就會被檢測到,我們我們需要將表單控件也都重置成未修改狀態(tài)。這在Angular2里很簡單,它提供了一個(gè)reset方法。

我們在里面添加一個(gè)重置按鈕:

<button (click)="reset(userForm)">重置</button>

然后在組件里:

reset(theForm: NgForm) {
  theForm.reset();
  return false;
}

注意我們需要讓這個(gè)方法返回false,這樣他就不會觸發(fā)submit的方法。

在官方的文檔中,還提供了另一種技巧來實(shí)現(xiàn)這種重置,就是在form上使用ngIf:

<form #userForm="ngForm" (ngSubmit)="logForm(userForm)" novalidate *ngIf="active">

只有在active為true時(shí)這個(gè)表單才會創(chuàng)建。

然后在重置的時(shí)候,設(shè)置這個(gè)active為false,這樣這個(gè)表單就會被銷毀,然后用setTimeout的方式再設(shè)置它為true,這個(gè)表單就會重新創(chuàng)建,這樣就實(shí)現(xiàn)了重置的效果。

reset() {
  this.user = { // 重置用戶數(shù)據(jù)
    address: {}
  };
  this.active = false;
  setTimeout(() => this.active = true, 0);
  return false;
}

感謝各位的閱讀!關(guān)于“Angular2如何實(shí)現(xiàn)模板驅(qū)動的表單”這篇文章就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,讓大家可以學(xué)到更多知識,如果覺得文章不錯(cuò),可以把它分享出去讓更多的人看到吧!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI