在使用 AngularJS 进行开发的时候,表单填写是一个很常见的需求,而表单验证又是比较让人头疼的部分,本文对此作一个总结。javascript
在 Angular 的视图中使用的 form 已经不是 HTML 中的普通 form 了,而是一个被 Angular 封装过的指令。它能够完成普通 form 没法实现的功能,好比 form 嵌套,并且自带强大的验证功能。css
Angular 在对表单进行校验的时候会使用 ngModelController
上的属性,若是不设置 ng-model
,则 Angular 没法知道 form.$invalid
这个值是否为真。后面在自定义验证有对它的介绍。html
本文在对表单的验证时使用了 ng-messages
,在文章最后也有对它的介绍。java
在 form 层面,可使用 ng-disabled
来控制提交按钮的状态,在 form 表单项所有验证经过前不可点击,下面介绍一下通用的表单项验证选项。angularjs
AngularJS 的 input 标签 自带的验证选项有如下这些。github
<input ng-model="" [name=""] [required=""] [ng-required=""] [ng-minlength=""] [ng-maxlength=""] [ng-pattern=""] [ng-change=""]> ... </input>
a. 必填api
<input type="text" name="myName" ng-model="username" required />
使用 ng-required
能够根据后面表达式的值设置是否 required
。promise
在不知足 required
时 form.myName.$error
为 {required: true}
。app
b. 长度
<input type="text" name="myName" ng-model="username" ng-minlength="2" ng-maxlength="10" />
在不知足 ng-minlength
/ng-maxlength
时 form.myName.$error
为 {minlength: true, maxlength: true}
。
直接使用 minlength
/maxlength
也有相同效果,并且 maxlength
能够设置最多输入 x 个字符,超过以后没法再输入。
c. 模式匹配
<input type="text" name="myDesc" ng-model="desc" ng-pattern="/^[a-zA-Z]{1,20}$/" />
在不知足 ng-pattern
时 form.myName.$error
为 {pattern: true}
。
d. 其余
AngularJS 对特定格式也进行了校验。好比将 type
设置为 url
,email
等,在没有特殊验证要求的状况下,能够直接使用这些自带的校验,或者经过自定义指令修改 Angular 内建验证器。不一样 type 有不一样的验证选项,具体参考 AngularJS API 文档。
Angular 会根据表单状态自动给表单和表单项添加如下几组样式:
ng-valid
验证经过,与之相对的是 ng-invalid
ng-valid-[key]
经过自定义验证器添加的验证经过的值,与之相对的是 ng-invalid-[key]
ng-pristine
未交互状态,与之相对的是 ng-dirty
ng-touched
未访问状态,与之相对的是 ng-untouched
ng-pending
知足 $asyncValidators
的状况
这些在 ngModelController 的属性中都有对应值。
根据这些 class,能够为不一样状态设置不一样的样式,好比这样:
input.ng-valid.ng-dirty { border-color: #78FA89; } input.ng-invalid.ng-dirty { border-color: #FA787E; }
在 AngularJS 指令入门 一文中,提到过经过 require 属性和 controller 参数,能够实现指令之间的交互。那么,在自定义指令中使用 require: 'ngModel'
就可使用 ngModel
指令的 controller 属性的实例了。
ngModel 提供了数据绑定、验证、CSS更新、数据格式化和编译等操做。下面简单介绍一下 ngModelController 经常使用的属性和方法。
$viewValue
视图里的值
$modelValue
数据模型里值
在 input
事件触发的时候,$viewValue
会同步到 $modelValue
。默认状况下,这个是一旦 input
中的内容有改变就触发。AngularJS 1.3 引入了 ng-model-options
,可让这个同步延迟到 blur 或者延迟必定的时间以后。
<input type="text" name="username" ng-model="username" ng-model-options="{updateOn:'blur'}"> <input type="text" name="username" ng-model="username" ng-model-options="{ debounce: 500 }">
在 $viewValue
的值同步到 $modelValue
时,会通过 $parsers
、$validators
和 $asyncValidators
三个核心管道(后两个是 AngularJS 1.3 之后新加的)进行处理,经过后才更新到 $modelValue
上(若是验证器管道没经过,不会更新)。
$parsers
改变视图值的格式,并更新的到模型($viewValue
-> $modelValue
),与之相对的是 $formatters
,恰好反过来。
$validators
用来添加同步验证器
$asyncValidators
用来添加异步验证器
$error
没有经过的验证器名称及对应的错误信息
$valid
表单项是否都经过验证,都经过时为 true,与之相对的是 $invalid
$touched
表单项是否被访问过,若是得到过焦点,在失去时该值为 true,与之相对的是 $untouched
$dirty
表示用户是否和表单项交互过(好比输入一些东西),只要有任何改变,该值为 true,与之相对的是 $pristine
$render
定义视图具体的渲染方式
$setViewValue
设置视图值(须要手动触发一个 $digest
),使用场景是在自定义指令中监听自定义事件(好比使用具备回调的 jQuery 插件)
在 ngModelController
中讲到,AngularJS 1.3 提供了验证器管道,同步验证只须要加到 $validators
上便可。
好比,有这样一个常见的需求,对一个必填的名称表单项,要求只能输入中英文,最小长度为2位字符,那么能够这样实现。
指令:
app.directive('nameCheck', nameCheck); nameCheck.$inject = ['$http', '$q']; function nameCheck($http, $q){ var NAME_REG = /^[a-zA-Z\u4e00-\u9fa5]+$/; return { restrict: 'A', require: 'ngModel', link:function($scope,element,attrs,ctrl){ // 同步验证 ctrl.$validators.char = function(modelValue, viewValue) { var value = modelValue || viewValue; if(!NAME_REG.test(value)){ return false; } return true; }; // 异步验证 ctrl.$asyncValidators.exist = function(modelValue, viewValue){ var value = modelValue || viewValue; var deferred = $q.defer(); $http.get('api/users/' + value).then(function(res) { if(res.data.isExist){ deferred.reject(false); } deferred.resolve(true); }) return deferred.promise; } } } }
主页面:
<form name="myForm"> <div class="form-group"> <input type="text" name="username" ng-model="username" class="form-control" name-check minlength="2" required> <span ng-messages="myForm.username.$error" ng-messages-include="error.html" ng-show="myForm.username.$touched"> </span> </div> </form>
错误信息页面:
<span ng-message="required">必填</span> <span ng-message="char">非法字符</span> <span ng-message="minlength">过短了</span> <span ng-message="exist">名称已存在</span>
ng-messages
是 AngularJS 1.3 提供的一个用来加强模版显示的模块,主要用在处理复杂的错误信息。
在之前的版本中,若是想处理错误信息的显示,可能须要定义一堆 code
再结合复杂的 ng-if
语句来实现。并且在输入同时知足多条错误规则的状况下,没法控制错误信息显示的优先级。这些,使用 ng-messages
能够完美解决。
引入 angular-messages.js
添加依赖:angular.module('app', ['ngMessages'])
有两种使用方法,一是将 ng-messages
看成属性指令使用:
<form name="myForm"> <input type="text" ng-model="field" name="myField" required minlength="5" /> <div ng-messages="myForm.myField.$error"> <div ng-message="required">必填</div> <div ng-message="minlength">长度不够</div> </div> </form>
这样会按照各个错误信息书写的前后顺序进行单一显示,若是想同时显示全部的错误信息,加个 ng-messages-multiple
:
<div ng-messages="myForm.myField.$error" ng-messages-multiple></div>
另外一种是将 ng-messages
看成元素指令使用:
<ng-messages for="myForm.myField.$error"> <ng-message when="required">必填</ng-message> <ng-message when="minlength">长度不够</ng-message> </ng-messages>
若是不少表单项的错误提示信息都同样,也能够把错误信息放在模版里,使用 ng-messages-include
指令来引用:
<div ng-messages="myForm.username.$error" ng-messages-include="validateTemplate/error.html"> </div>
错误模版文件:
<div ng-message="required">必填</div> <div ng-message="minlength">长度不够</div>
更详细的使用办法直接看 angular-messages.js
源文件里面的注释便可。