年末了愈来愈懒散,AngularJs的学习落了一段时间,博客最近也没更新。惭愧~前段时间有试了一下用yeoman构建Angular项目,感受学的差很少了想作个项目练练手,谁知遇到了一系列问题。yeoman是基于node.js的一套工具包,因为我一直在windows下编程,并且node.js对于windows环境的支持也在慢慢增强,因此想尝试在windows下用yeoman跟搭建一个项目。过程远比想象的坎坷多了,各类报错,各类搜资料解决问题,最终仍是没法解决一些编译出错,以失败了结,转战Linux。在此也提醒你们若是想在windows下使用yeoman,仍是谨慎为好!css
今天来学习一下一直被我忽视掉的表单验证。ng的强项是开发CRUD应用,也就是与用户操做多、交互比较频繁的应用。表单是与用户交互的一个重要角色,因此万万不能忽视。在学习以后发现这部分知识不只仅是想象中的那么简单,比起其余特性,咱们一直不怎么重视的表单验证,其实也能够作的很简单,并且易维护。下面就开始吧~html
看这个小标题也行你会差别,表单验证,怎么跟controller扯上关系了。ng中的form已经不一样于咱们平时用的form标签,作了加强。form是FormController的一个实例。如何理解这句话呢?想一想咱们使用ng-controller指令的情景:node
<div ng-controller="testC"> <input type="test" ng-model="a" /> </div> <scritp> function testC($scope){ //............. } </script>
应用了ng-controller的div就是testC的一个实例,咱们能够在模板中使用定义在$scopt上的任何属性和方法,而testC的定义也是由咱们本身实现的。当咱们使用<form>的时候也是这样的道理,FormController由ng为咱们定义好了,有一系列属性和方法提供给咱们完成验证工做,form实例经过name属性来进行标识,咱们能够经过此标识来访问form实例的属性和方法,如:angularjs
<form name="myform"> {{myform.$valid}} </form>
form提供的属性都是用来表示表单的验证状态的,包括:$pristine(表单没有填写记录)、$dirty(表单有填写记录)、$valid(经过验证)、$invalid(未经过验证)、$error(验证错误信息)。除$error外,前四个的值为true或false表示相应的状态。$error的值为一个js对象,包含了如下验证内容的状态:编程
email
max
maxlength
min
minlength
number
pattern
required
url
这些内容咱们会在稍后的例子中看到。FormController还提供了一些方法,咱们通常不手工调用它们,都是系统本身调用。可参考官方文档:http://docs.angularjs.org/api/ng.directive:form.FormControllerwindows
表单元素,如input、checkbox、radio等也不是普通的表单元素了,它们统统是NgModelController的实例。与form同样,也是经过name属性来标识。FormController拥有的那五个属性,NgModelController也一样拥有,除此以外,还有许多额外的属性和方法,咱们稍后也在示例中展现,可参考官方文档:http://docs.angularjs.org/api/ng.directive:ngModel.NgModelControllerapi
还有一个特性须要了解,一个表单中的表单元素,会做为这个form的属性自动加在上面,经过name标识就能够访问到,如:数组
<form name="myform"> <input type="text" name="myname" /> {{myform.myname.$valid}} </form>
ng框架提供了很是方便的验证机制,你只须要在标签上加点指令,像使用HTML5提供的验证那样,而后在css中根据规则定义好正确/错误的样式就OK了,例如咱们要让一个文本框为必填项,使用required:浏览器
<form name="myform novalidate> <input type="text" ng-model="a" required /> </form>
有几点须要注意:app
这部分仍是至关简单的,下面咱们写例子来测一下这几种验证机制,HTML代码以下:
<div ng-app="MyApp"> <div ng-controller="testC"> <form name="myform" novalidate> required: <input type="text" name="test1" ng-model="test1" required><br /> ng-minlength(3): <input type="text" name="test2" ng-model="test2" ng-minlength="3"><br /> ng-maxlength(10): <input type="text" name="test3" ng-model="test3" ng-maxlength="10"><br /> ng-pattern(/[a-f]/): <input type="text" name="test4" ng-model="test4" ng-pattern="/[a-f]/"><br /> type="number"(2-8): <input type="number" name="test5" max="8" min="2" ng-model="test5"><br /> type="url": <input type="url" name="test6" ng-model="test6"><br /> type="email": <input type="email" name="test7" ng-model="test7"><br /> </form> <div> <h2>表单验证结果:</h2> myform.$invalid : {{myform.$invalid}}<br /> myform.$valid : {{myform.$valid}}<br /> myform.$pristine : {{myform.$pristine}}<br /> myform.$dirty : {{myform.$dirty}}<br /> myform.$error : {{myform.$error}}<br /> <h2>表单项验证结果</h2> required:<br /> myform.test1.$invalid : {{myform.test1.$invalid}}<br /> myform.test1.$valid : {{myform.test1.$valid}}<br /> myform.test1.$pristine : {{myform.test1.$pristine}}<br /> myform.test1.$dirty : {{myform.test1.$dirty}}<br /> myform.test1.$error : {{myform.test1.$error}}<br /> myform.test2.$error : {{myform.test2.$error}}<br /> </div> </div> </div>
CSS代码,为不一样的状态设置不一样的背景色:
input.ng-pristine { background-color: white; } input.ng-dirty { background-color: lightyellow; } input.ng-valid { background-color: lightgreen; } input.ng-invalid { background-color: pink; }
js代码,进行controller的初始化:
var app = angular.module('MyApp',[]); app.controller('testC',function($scope){ $scope.test1=''; $scope.test2=''; $scope.test3=''; $scope.test4=''; $scope.test5=''; $scope.test6=''; $scope.test7=''; });
结果以下:
该示例编写在runjs上,点击查看http://runjs.cn/code/gspvlfrw
在上面的代码中,你也看到了我从FormConroller实例myform访问到的属性,还有从NgModelController访问到的属性。这些属性是很是有用的,好比你能够给表单的按钮加上:ng-disabled="myform.$invalid",这样在表单未经过验证的时候,提交按钮始终是不可点的。另外也能够根据表单元素的这些属性,来控制具体的错误提示信息,好比邮箱输错了,让"请输入正确的邮箱“这行字显示出来,若是你顺着个人思路,应该立马能想象到。
除了内置的这些验证规则,你还能够本身定义。方法就是写一个指令,加在表单元素上。听起来好简单的样子,可是这指令与通常的指令可不一样,咱们须要按必定的规则来写,这样才能够融入ng的验证机制,让你自定义的跟内置的同样能够便捷的工做和管理。这个时候,NgModelController提供的方法就派上用场了。咱们从一个例子来开始吧。
我想让个人输入框只容许输入偶数,咱们来定义一个名为even-num的指令,在页面上使用的时候像这样:
<input type="number" ng-model="test1" even-num />
完整的js代码以下:
var app = angular.module('MyApp',[]); app.controller('testC',function($scope){ $scope.test1 = ''; }); app.directive('evenNum',function(){ return { require: 'ngModel', link: function(scope, elm, attrs, ctrl) { ctrl.$parsers.push(function(viewValue) { if (viewValue % 2 == 0) { ctrl.$setValidity('evenNum', true); return viewValue; } else { ctrl.$setValidity('evenNum', false); return viewValue; } }); } }; });
运行结果:
上面的例子编写在runjs上,点击查看http://runjs.cn/code/gdq8m0gb
如今来解释一下上面的代码。自定义指令的方式若是你不熟悉,能够先看一下我以前写的自定义指令部分。由于咱们的指令要依赖NgModelController,因此写上了require:'ngModel',注意书写方式。 另外在link函数中,经过ctrl引用到了咱们注入的NgModelController,而后向它的$parsers属性中push了一个函数进去。这个$parsers是什么东西呢?很明显它是一个数组,由于咱们能够push东西进去。在解释以前,咱们先清楚两个概念:咱们把模板中的数据,像{{aa}}这样的,叫作viewValue,故名思义,视图中的数据。咱们把模型/controller中的数据,叫作modelValue。ng所说的双向绑定,就是把这二者进行绑定。这个$parsers保存了从viewValue向modelValue绑定过程当中的处理函数,它们未来会依次执行。由于咱们的验证是从用户输入开始,即view发生了变化,因此咱们的验证逻辑就加在这里。在验证结果中,咱们调用ctrl.$setValidity方法,将结果保存,这样框架就能完成接下来的一系列工做。
与$parsers相对的,还有一个属性叫$formatters,它保存的是从modelValue向viewValue绑定过程当中的处理函数。那咱们定义的这个验证函数,要不要也push进$formatters里去呢?这取决于你的须要。若是你对二者的区别还不太清楚,看了下面这个例子就明白了:
上面的例子编写在runjs上,点击查看http://runjs.cn/code/9vde2r0w
两个input的ng-model指向的是同一个,因此数据会同步变化,可是$formatters里没有push进去验证函数,因此在从modelValue向ViewValue绑定的过程当中,副本并无进行验证。若是把验证函数push进$formatters,那么副本也会跟着验证了。
咱们都知道,在表单元素上使用ng-model能够进行双向绑定。可是双向绑定只能用于input、checkbox这些标准表单控件上,你给一个div加ng-model是不能双向绑定的,由于系统不知道该如何绑定。因此话说过来,要想给非标准表单控件双向绑定,代码还得本身来写,说白了就是自定义一个指令。其实这部份内容放在自定义指令中也是合适的,可是官网在这里提到了,我也来介绍一下。
咱们直接从例子开始,你们必定见过自适应的文本区域,就是随着输入内容的增长,会自动变高的textarea。用<textarea>标签作的话,须要加js代码才能够实现。更好的是纯css的方案,使用HTML5的新属性contenteditable,用一个div来模拟文本区域,div的高度默认就是自适应的,正好能够知足需求。基本的HTML代码和css代码以下:
<style> .smarttextarea{ width: 400px; min-height: 100px; max-height: 400px; border: 1px solid; overflow: auto; padding:5px 10px 20px; } </style> <div contenteditable=”true" class="smarttextarea"></div>
咱们如今要作的就是,让这个模拟出来的文本区域跟真正的textarea那样,能够进行数据的双向绑定,这样就能够进行验证了。我定义了一个名为smarttextarea的指令,使用起来像这样:
<smarttextarea contenteditable="true" class="smarttextarea" ng-model="test3" required></smarttextarea>
指令的定义以下:
app.directive('smarttextarea',function(){ var link = function(scope, elm, attrs, ctrl) { //view=>model数据绑定 elm.bind('keyup', function() { scope.$apply(function() { ctrl.$setViewValue(elm.html()); }); }); //model=>view数据绑定 ctrl.$render = function() { elm.html(ctrl.$viewValue); }; ctrl.$setViewValue(elm.html()); }; return { template : '<div></div>', replace : true, require: 'ngModel', restrict: 'E', link : link }; });
看一下效果:
上面的例子编写在runjs上,点击查看http://runjs.cn/code/lhysp5vh
我给模拟出来的textarea加了required验证,能够发现生效了。其实关键代码就是进行了数据的双向绑定处理,包括两步:
通过这两步,咱们就自定义了一个跟标准表单控件同样的元素,能够进行数据的双向绑定,表单验证统统没有问题。