未加星标

Angular2表单-模板驱动的表单(Template-Driven Forms)

字体大小 | |
[前端(javascript) 所属分类 前端(javascript) | 发布者 店小二03 | 时间 2016 | 作者 红领巾 ] 0人收藏点击收藏

在网页开发中,表单估计是最常用的一个,同时也是最麻烦、最容易出问题的。在一个稍微复杂一点的应用中,我们除了用表单元素收集数据,还需要验证,几个数据之间可能还会相互关联,然后根据不同的数据值调用不同的业务逻辑等。

使用Angular提供的数据绑定的功能,我们可以很容易就在组件中获得用户输入的数据,Angular也提供了几种验证方式方便我们进行数据的校验。但是,一些自定义的数据验证、数据交互和业务逻辑还是需要自己处理。

在Angular2中,提供了2种表单实现方式,分别是’template-driven’(模板驱动的表单)和’model-driven’(模型驱动表单)。在这篇文章中,我们先来看看模板驱动的表单。顾名思义,模板驱动的表单就是大部分表单相关代码都在模板里,通过在模板里面添加ngForm, ngModel和ngModelGroup等属性来定义模板和验证信息,以及它跟组件之间的数据交互。

实例

下图是这篇文章使用的实例的界面:


Angular2表单-模板驱动的表单(Template-Driven Forms)

它是一个用户信息输入的表单,包括4个字段,用户名、电话、城市和街道,演示了如何使用表单,给各个字段添加验证并显示验证结果,以及如何在组件中判断是否出错并获取出错信息。

项目源码可以从 github 获取,这个项目包含了几个Angular2表单相关的实例,可以使用下面的命令获取本文所对应的代码:

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

然后进入项目目录,运行下面的命令安装依赖然后运行测试服务器:

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

该项目是基于之前的 Angular2-basic 模板,这个教程相关的代码都在’template-forms’目录里面。

引入FormsModule

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

import{ FormsModule [email protected]/forms'; //省略其他 @NgModule({ imports: [ BrowserModule, FormsModule ], //省略其他 }) 初始表单

然后,我们从一个基本的html表单开始:

<form> <label>姓名:</label> <inputtype="text"> <label>电话:</label> <inputtype="text"> <label>城市:</label> <inputtype="text"> <label>街道:</label> <inputtype="text"> <buttontype="submit">保存</button> </form>

在实际的实例中,使用了bootstrap的表单样式,一组输入框应该是下面这个样子,但是在本文中,为了节省页面显示的篇幅,我省略了div, form-group等,我们只需要关心如何在Angular2中使用模板驱动的表单。如果想查看完整的带样式的代码,请查看 源文件 。

<divclass="form-group"> <labelclass="col-sm-2 control-label">姓名:</label> <divclass="col-sm-10"> <inputclass="form-control"type="text"> </div> </div> ngForm

在上面的表单里,我们没有使用Angular2的任何功能,如数据绑定,也没有使用其他指令。但是,Angular2在 <form> 上实现了一个指令’ngForm’,这样,对于所有的html的form表单,都会使用ngForm组件去初始化该表单。

使用ngForm对象

接下来,我们需要在模板里面访问这个ngForm的实例,这样我们就能够从这个实例里面获取数据,或者获取数据验证状态。

在Angular2里,都提供了一个 模板引用变量 的功能,通过 # 加变量实现。通过这个功能,我们可以在同一元素、兄弟元素或任何子元素中引用模板引用变量。这样听着还是不好理解,我们看一个例子:

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

在这个例子中,我们通过 #phone 定义了一个变量,它所指的就是这个input元素, phone.value 也就是这个输入框输入的值。

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

我们可以对任何的DOM元素使用这种方式获取当前引用,也可以对任何的Angular2的指令使用。在这个表单的例子中,我们这样来获取这个ngFrom的引用:

<form#userForm="ngForm">

其中’ngForm’就是当前这个指令,这样在这个模板里面,我们可以用 userForm 获得表单的所有数据。

提交表单

在html中,我们要提交一个form,会在form里写一个action的属性,然后,用一个类型为’submit’的按钮来提交。但是,在Angular2中,我们需要使用 ngSubmit 事件:

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

这样,当用户点击保存按钮的时候,Angular2会使用自己的验证机制,验证所有的数据,然后在调用’logForm(userForm)’方法。

在我们的组件中,实现这个方法:

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

在这个方法里,我们使用 theForm.invalid 就可以获得这个表单是否验证成功的状态,也可以用’theForm.value’获得所有的表单数据。在这里,我们把表单数据打印到控制台来检查数据。至于如何从这个表单引用中获取控件数据和状态,会在接下来再讲。

使用ngModel绑定数据

接下来,我们需要绑定数据。假设我们的业务是打开这个页面的时候获取用户数据,然后显示到页面表单上。我们在组件的构造方法中创建一个模拟的用户数据:

exportclassTemplateFormsComponent{ user: any; constructor() { this.user = { name: '张三', mobile: 13800138001, city: '北京', street: '朝阳望京...' }; } }

然后在模板中将这个组件中的数据绑定到模板页面上:

<inputtype="text"name="name"[(ngModel)]="user.name"> <inputtype="text"name="mobile"[ngModel]="user.mobile"> <inputtype="text"name="city"[ngModel]="user.city"> <inputtype="text"name="street"[ngModel]="user.street"> <!-- 其他的输入框都类似 --> 在这里,我们使用 [(ngModel)]="user.name" ,这是双向绑定的方式,这样,当我们修改页面上的数据的时候,在组件中也能获得更新后的数据;同时,如果在组件中更新了数据,在页面上也能更新。 为了演示这个双向绑定跟单向绑定的区别,我们只对姓名使用双向绑定,对其他的都是用单向绑定,也就是 [ngModel]="user.mobile" 。使用 [] 的单向绑定是从模板到组件的绑定,也就是页面中的输入的数据改变,组件中的数据也会改变。但是组件中的数据更新不会引起页面上该数据的更新。

使用单向绑定可以减少数据的更新检查,从来可以提高性能。

如果不需要数据的初始化,我们其实可以只用 ngModel ,例如:

<inputtype="text"name="city"ngModel>

这样,我们在组件中创建的用户数据就无法显示到页面上,但是,他还是能够将页面上输入的数据绑定到组件中的数据上。

在Angular2中,使用 ngModel 结合 name 属性来创建一个表单控件 FormControls 。例如上面的 <input name="city" ngModel> 就对应一个 userForm 里面的控件 city 。由于我们在提交方法里面将这个 userForm 作为参数传到方法里,我们可以在方法里面获得所有的表单控件 theForm.controls ,它是一个Map类型的对象,key是所有的表单元素的 name ,值就是一个 FormControl 对象,里面保存着数据、和验证结果、是否修改等状态。也正是因为这些 FormControls ,我们才能够使用 theForm.value 的方式获取表单里的数据。当我们点击保存按钮的时候,就能在日志里面看到表单的数据:

{ name: "张三", mobile: 13800138001, city: "北京", street: "朝阳望京..." } 使用ngModelGroup分组显示

一般情况下,我们的model数据有可能是嵌套的,比如对于用户信息来说,城市和街道可能在一个地址对象 address 里,例如:

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

对于这样的数据,我们就可以使用ngModelGroup来分组。模板就是这样:

<form#userForm="ngForm"(ngSubmit)="logForm(userForm)"> <label>姓名:</label> <inputtype="text"name="name"[(ngModel)]="user.name"> <label>电话:</label> <inputtype="text"name="mobile"[ngModel]="user.mobile"> <fieldsetngModelGroup="address"> <label>城市:</label> <inputtype="text"name="city"[ngModel]="user.address.city"> <label>街道:</label> <inputtype="text"name="street"[ngModel]="user.address.street"> <buttontype="submit">保存</button> </fieldset> </form>

这样我们就把地址信息都封装到一个address对象里面。注意我们绑定的数据的结构也发生改变,这样,我们也需要修改我们的组件里面的用户数据:

exportclassTemplateFormsComponent{ user: any; constructor() { this.user = { name: '张三', mobile: 13800138001, address: { city: '北京', street: '朝阳望京...' } }; } }

至此,我们的表单的基本功能就算完成了。我们在面板中创建了表单,在组件中初始化了用户数据,并显示到页面上,在页面上用ngModel,将页面上的数据更改绑定到组件上。同时,使用 name 属性,使得表单里面的所有数据都成为 FormControl 对象。在提交所调用的方法里,获得了表单的验证状态和数据。

表单控件的验证和状态

下一步,我们来添加数据验证,Angular2为我们提供了几种最基本的验证:

required:表明该数据是必须的。 minlength:设置该字段的长度的最小值,即使输入的是数字,也按照字符串来判断长度。 maxlength:设置该字段的长度的最大值。 pattern:使用正则表达式验证

在使用Angular的验证之前,我们首先需要关闭浏览器默认的验证,不然,如果某一个输入不合法,提交按钮就无法提交。我们在 form 里添加 novalidate :

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

然后,我们对姓名输入框添加验证,并根据验证的结果显示不同的提示,同时,为了演示Angular2表单控件的特性,再添加几个提示,来显示该值的状态,代码如下:

<inputtype="text"name="name"[(ngModel)]="user.name"#name="ngModel"requiredminlength="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个验证, required 和 minlength="3" 。

其次,我们使用 #name="ngModel" 创建了一个模板引用变量,这样我们在下面就可以使用 name 来获取这个表单控件( FormControl )的引用。表单控件有一些属性,如 pristine , dirty , valid , touched ,这几个都是状态类型,表示某一种状态是否为真。除此以外还有控件的值可以用 name.value 获取。最后,还有验证的错误信息结果,会放在 name.errors 里。

在上面的代码里,我们用 <span *ngIf="name.pristine" class="label label-primary">未修改</span> ,在控件值未被修改的时候,显示一个lebel。同样,在被修改、验证有效的时候显示相应的标签。

最后,所有的验证结果的错误信息会保存在 name.errors 里,如果没有数据验证错误,这个errors值就是 null ,所以,在上面的代码里,我们用 name.errors?.minlength ,这表示,如果 errors 不为null,而且 errors.minlength 也不为空的时候,才显示里面的信息。

我们可以看到,表单控件的验证会将验证器的名字作为key放在 errors 里面,对应的值是 true 。我们就是用这个特性,来根据控件验证的不同结果,来显示友好的错误信息。

如果运行我们的实例,可以发现,对于姓名,如果清空它的值,发现只有一个错误信息,就是 必须输入姓名 。你可能会觉得,这时候,值为空,那他的长度也小于3,那么 minlength 这个错误也应该被检测到才对,但是实际上,遇到第一个错误以后,就没有其他的验证。

在上面姓名输入框上,我们使用 #name="ngModel" 创建了一个模板引用变量,然后在接下来的模板里面使用它获得表单控件。实际上,我们也可以直接使用之前定义的对 ngForm 的引用,来获得这个表单里所有控件的状态。例如,对电话,我们使用下面的方式:

<inputtype="text"name="mobile"[ngModel]="user.mobile"requiredminlength="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

当获取验证错误结果时:

userForm.controls.mobile?.errors?.minlength

注意这里在mobile上就使用 ? 是因为,在使用 ngIf 渲染页面上的元素的时候,这个表单控件还没有初始化完成,如果不加这个 ? ,就会出现错误。

根据验证状态定义样式

Angular的表单验证,除了在控件上的数据以外,它还会根据状态在控件所在的html元素上添加css样式:


Angular2表单-模板驱动的表单(Template-Driven Forms)

所以,我们只需要定义相关的css,就可以实现根据状态显示不同的效果。

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

结合各种css的选择器,我们就可以根据表单控件的状态实现各种显示的样式。

在组件中获取表单控件数据

最后,我们再看看怎样在组件中获取这些控件的状态和结果,在上面,我们给 ngForm 添加了一个提交方法:

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

然后在组件中,这个 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); }

在这个方法里, theForm 就是 ngForm 的模板引用实例,类型是 NgForm 的。

如果表单验证有失败, theForm.invalid 就是false。

theForm.controls 就是这个表单里的所有控件,如果想获取姓名的验证结果,就是 theForm.controls['name'].errors 。

用这种方式,我们就可以在组件中获取所有表单控件的数据、验证状态、错误信息等。

重置表单

一般情况下,如果是新建用户信息,我们需要在保存成功以后,清空当前数据,重置表单的状态,等待用户重新输入。如果我们只是清空数据,这时候那些验证错误就会被检测到,我们我们需要将表单控件也都重置成未修改状态。这在Angular2里很简单,它提供了一个 reset 方法。

我们在

里面添加一个重置按钮:

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

然后在组件里:

reset(theForm: NgForm) { theForm.reset(); returnfalse; }

注意我们需要让这个方法返回false,这样他就不会触发submit的方法。

在官方的文档中,还提供了另一种技巧来实现这种重置,就是在form上使用 ngIf :

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

只有在 active 为true时这个表单才会创建。

然后在重置的时候,设置这个 active 为false,这样这个表单就会被销毁,然后用 setTimeout 的方式再设置它为true,这个表单就会重新创建,这样就实现了重置的效果。

reset() { this.user = {// 重置用户数据 address: {} }; this.active =false; setTimeout(() => this.active =true,0); returnfalse; }

这也是一种小窍门,可以在某些情况下使用。

总结

至此,有关模型驱动的表单的基本用法大致完成,再总结一下模型驱动的表单的基本特性:

所有的表单控件的定义都在模板里 所有的验证器都在模板里面添加 表单数据的状态、验证结果都在模板上通过判断表单里面控件数据的状态来显示 如果需要测试这部分的代码,需要使用e2e(端到端)测试,也就是在浏览器里面

本文前端(javascript)相关术语:javascript是什么意思 javascript下载 javascript权威指南 javascript基础教程 javascript 正则表达式 javascript设计模式 javascript高级程序设计 精通javascript javascript教程

分页:12
转载请注明
本文标题:Angular2表单-模板驱动的表单(Template-Driven Forms)
本站链接:http://www.codesec.net/view/484759.html
分享请点击:


1.凡CodeSecTeam转载的文章,均出自其它媒体或其他官网介绍,目的在于传递更多的信息,并不代表本站赞同其观点和其真实性负责;
2.转载的文章仅代表原创作者观点,与本站无关。其原创性以及文中陈述文字和内容未经本站证实,本站对该文以及其中全部或者部分内容、文字的真实性、完整性、及时性,不作出任何保证或承若;
3.如本站转载稿涉及版权等问题,请作者及时联系本站,我们会及时处理。
登录后可拥有收藏文章、关注作者等权限...
技术大类 技术大类 | 前端(javascript) | 评论(0) | 阅读(92)