angular学习(十一)—— Form
来源:互联网 发布:几点睡觉 知乎 编辑:程序博客网 时间:2024/04/29 11:45
转载请写明来源地址:http://blog.csdn.net/lastsweetop/article/details/54972551
form
control主要用于接收用户输入,例如input, select, textarea。而form是一组相关控件的集合。form和control提供验证服务,可以在用户提交表单到后端前得到无效输入的通知。
一个简单的form
form的验证要比服务端的验证好很多,因为更加及时得到反馈以便纠正错误。虽然客户端的验证体验很好,但是可以很容易做到规避验证,服务端的验证还是非常必要的,特别是那些安全要求高的应用。
理解angularjs双向绑定的关键点在于指令ngModel,ngModel提供了视图到模型,模型到视图的双向绑定,它还提供了一些api给其他指令,用于增强它的作用。
<!DOCTYPE html><html><head> <meta charset="uft-8"/> <title></title></head><script src="script/angular.min.js"></script><body ng-app="formExample"><div ng-controller="ExampleController"> <form novalidate class="simple-form"> <label>Name: <input type="text" ng-model="user.name" /></label><br /> <label>E-mail: <input type="email" ng-model="user.email" /></label><br /> Best Editor: <label><input type="radio" ng-model="user.preference" value="vi" />vi</label> <label><input type="radio" ng-model="user.preference" value="emacs" />emacs</label><br /> <input type="button" ng-click="reset()" value="Reset" /> <input type="submit" ng-click="update(user)" value="Save" /> </form> <pre>user = {{user | json}}</pre> <pre>master = {{master | json}}</pre></div><script> angular.module('formExample', []) .controller('ExampleController', ['$scope', function($scope) { $scope.master = {}; $scope.update = function(user) { $scope.master = angular.copy(user); }; $scope.reset = function() { $scope.user = angular.copy($scope.master); }; $scope.reset(); }]);</script></body></html>
注意novalidate
可以禁用浏览器对表单进行原生性的校验。
除非输入通过校验,否则ngModel对应的值不会被设置。比如email类型的输入框必须使用user@domain的格式。
使用css class
为了表单和控件一样都有样式,ngModel增加了一些css class:
- ng-valid:model有效
- ng-invalid:model无效
- ng-valid-[key]:通过$setValidity添加的每一个有效的key
- ng-invalid-[key]:通过$setValidity添加的每一个无效的key
- ng-pristine:控件值为初始状态
- ng-dirty:控件的值已经做过了修改
- ng-touched:控件已经被点击过并失去焦点
- ng-untouched:控件还未被点击过
- ng-pending:还有$asyncValidators未完成。
下面的例子使用css来显示控件的输入有效性。在这个例子中,user.name和user.email是必须输入的,但是只有当输入是模糊的时候(控件被点击并失去焦点之后)才会显示红色的背景,这确保了用户只有在和控件交互之后,而输入无法满足验证条件,才会提示错误,而在交换之前说不会提示错误的。
<!DOCTYPE html><html><head> <meta charset="uft-8"/> <title></title></head><script src="script/angular.min.js"></script><body ng-app="formExample"><div ng-controller="ExampleController"> <form novalidate class="css-form"> <label>Name: <input type="text" ng-model="user.name" required /></label><br /> <label>E-mail: <input type="email" ng-model="user.email" required /></label><br /> Gender: <label><input type="radio" ng-model="user.gender" value="male" />male</label> <label><input type="radio" ng-model="user.gender" value="female" />female</label><br /> <input type="button" ng-click="reset()" value="Reset" /> <input type="submit" ng-click="update(user)" value="Save" /> </form> <pre>user = {{user | json}}</pre> <pre>master = {{master | json}}</pre></div><style type="text/css"> .css-form input.ng-invalid.ng-touched { background-color: #FA787E; } .css-form input.ng-valid.ng-touched { background-color: #78FA89; }</style><script> angular.module('formExample', []) .controller('ExampleController', ['$scope', function($scope) { $scope.master = {}; $scope.update = function(user) { $scope.master = angular.copy(user); }; $scope.reset = function() { $scope.user = angular.copy($scope.master); }; $scope.reset(); }]);</script></body></html>
form和control的状态绑定
在angualr中,form是FormController的实例,form实例可以通过name属性发布到scope中。同样,带有ngModel指令的输入控件一样也是NgModelController实例,一样可以通过控件的name属性发布,作为form实例的一个属性。
这意味着form和控件的内部状态一样可以通过标准的绑定方式在视图中绑定。
我们来扩展一下上面的例子:
- 在用户和控件交互之后,显示自定义的错误信息
- 用户提交表单时显示自定义错误信息,即使用户和控件没有交互
<!DOCTYPE html><html><head> <meta charset="uft-8"/> <title></title></head><script src="script/angular.min.js"></script><body ng-app="formExample"><div ng-controller="ExampleController"> <form novalidate class="css-form" name="form"> <label>Name: <input type="text" ng-model="user.name" name="uName" required="" /></label><br /> <div ng-show="form.$submitted || form.uName.$touched"> <div ng-show="form.uName.$error.required">Tell us your name.</div> </div> <label>E-mail: <input type="email" ng-model="user.email" name="uEmail" required /></label><br /> <div ng-show="form.$submitted || form.uEmail.$touched"> <span ng-show="form.uEmail.$error.required">Tell us your email.</span> <span ng-show="form.uEmail.$error.email">This is not a valid email.</span> </div> Gender: <label><input type="radio" ng-model="user.gender" value="male" />male</label> <label><input type="radio" ng-model="user.gender" value="female" />female</label><br /> <label> <input type="checkbox" ng-model="user.agree" name="userAgree" required="" /> I agree: </label> <input ng-show="user.agree" type="text" ng-model="user.agreeSign" required="" /> <br /> <div ng-show="form.$submitted || form.userAgree.$touched"> <div ng-show="!user.agree || !user.agreeSign">Please agree and sign.</div> </div> <input type="button" ng-click="reset(form)" value="Reset" /> <input type="submit" ng-click="update(user)" value="Save" /> </form> <pre>user = {{user | json}}</pre> <pre>master = {{master | json}}</pre></div><style type="text/css"> .css-form input.ng-invalid.ng-touched { background-color: #FA787E; } .css-form input.ng-valid.ng-touched { background-color: #78FA89; }</style><script> angular.module('formExample', []) .controller('ExampleController', ['$scope', function($scope) { $scope.master = {}; $scope.update = function(user) { $scope.master = angular.copy(user); }; $scope.reset = function(form) { if (form) { form.$setPristine(); form.$setUntouched(); } $scope.user = angular.copy($scope.master); }; $scope.reset(); }]);</script></body></html>
自定义model更新触发器
默认情况下,对内容对任何更改都会触发model的改变和表单的验证。你可以通过ngModelOptions指令绑定到特殊的事件列表中以重写这种行为。ng-model-options="{ updateOn: 'blur' }"
表示仅仅在用户输入并且失去焦点时才会更新model并且进行表单验证。如果是多个事件,可以使用空格分开,例如:ng-model-options="{ updateOn: 'mousedown blur' }"
如果想保留默认的行为,而只是在默认事件上添加新的事件可以触发更新和验证,那么只需添加default
作为一个普通的事件之一。 ng-model-options="{ updateOn: 'default blur' }"
下面是个重写立即更新对例子。只有在用户和输入框产生交互时(点击控件并且失去焦点时),输入框的更改才会更新到model中。
<!DOCTYPE html><html><head> <meta charset="uft-8"/> <title></title></head><script src="script/angular.min.js"></script><script> angular.module('customTriggerExample', []) .controller('ExampleController', ['$scope', function($scope) { $scope.user = {name:"aa"}; }]);</script><body ng-app="customTriggerExample"><div ng-controller="ExampleController"> <form> <label>Name: <input type="text" ng-model="user.name" ng-model-options="{ updateOn: 'blur' }" /></label><br /> <label> Other data: <input type="text" ng-model="user.data" /></label><br /> </form> <pre>username = "{{user.name}}"</pre> <pre>userdata = "{{user.data}}"</pre></div></body></html>
延时model更新
你也可以通过ngModelOptions指令的debounce键延时更新和验证。这个延迟对于解析器,验证器和model的标签($dirty或$pristine.)同样起作用。
ng-model-options="{ debounce: 500 }"
表示将最后一次内容的改变触发model更新和form验证之前要等上500毫秒。
如果使用了自定义的触发器,那么使用debounce对象对每一个事件加上自定义的消抖超时时间。这在某些特定情况下还是有用的,比如blur事件后需要立即更新和验证。语法如下: ng-model-options="{ updateOn: 'default blur', debounce: { default: 500, blur: 0 } }"
注意,如果这些属性添加到某个元素中,那么这个元素的所有子元素和控件都会继承他的设定,除非子元素和控件重写这些属性。
下面的例子演示的是model更改的消抖,model将会在内容最后一次更改250毫米后进行更新。
<!DOCTYPE html><html><head> <meta charset="uft-8"/> <title></title></head><script src="script/angular.min.js"></script><script> angular.module('debounceExample', []) .controller('ExampleController', ['$scope', function($scope) { $scope.user = {}; }]);</script><body ng-app="debounceExample"><div ng-controller="ExampleController"> <form> <label>Name: <input type="text" ng-model="user.name" ng-model-options="{ debounce: 250 }" /></label><br /> </form> <pre>username = "{{user.name}}"</pre></div></body></html>
自定义验证
AngularJS为html5最常用的输入类型(text, number, url, email, date, radio, checkbox)提供了基本的实现,以及一些用于验证的指令(required, pattern, minlength, maxlength, min, max)。
如果想自定义验证的指令,你需要在ngModelController实例的$validators对象中增加你自定义的验证函数。可以参照下面的例子。
$validators对象的每一个验证函数都有两个参数modelValue和viewValue,angularjs会在内部调用$setValidity函数,参数就是这个验证函数的返回值。每次输入发生变化($setViewValue被调用)或约束模型发生变化时,验证函数都会执行。
此外,$asyncValidators对象处理异步的验证,比如需要通过http到后台请求验证数据的情况。添加到这个对象的验证函数必须返回一个promise,在有效时候resolve,无效时候reject。在异步验证的过程中,验证的key会被保存到ngModelController.$pending中。
下面的例子创建了两个验证指令:
integer指令验证输入是否是一个整数。例如,1.33就是个无效值。注意我们验证的事viewValue,控件中输入的值,而不是modleValue,这是因为类型为number的输入框会通过$parsers将viewValue转化为数字,如果我们输入的事1.00,那么modelValue将会是1,而viewValue是1.00,显然是无效的
username指令,异步检查用户输入的值是否已经存在,我们用$q.defer()来模拟服务器请求。
<!DOCTYPE html><html><head> <meta charset="uft-8"/> <title></title></head><script src="script/angular.min.js"></script><body ng-app="form-example1"><form name="form" class="css-form" novalidate> <div> <label> Size (integer 0 - 10): <input type="number" ng-model="size" name="size" min="0" max="10" integer />{{size}}</label><br /> <span ng-show="form.size.$error.integer">The value is not a valid integer!</span> <span ng-show="form.size.$error.min || form.size.$error.max"> The value must be in range 0 to 10!</span> <span ng-show="form.size.$error.require">111</span> </div> <div> <label> Username: <input type="text" ng-model="name" name="name" username />{{name}}</label><br /> <span ng-show="form.name.$pending.username">Checking if this name is available...</span> <span ng-show="form.name.$error.username">This username is already taken!</span> </div></form></body><script> var app = angular.module('form-example1', []); var INTEGER_REGEXP = /^-?\d+$/; app.directive('integer', function() { return { require: 'ngModel', link: function(scope, elm, attrs, ctrl) { ctrl.$validators.integer = function(modelValue, viewValue) { if (ctrl.$isEmpty(modelValue)) { // consider empty models to be valid return true; } if (INTEGER_REGEXP.test(viewValue)) { // it is valid return true; } // it is invalid return false; }; } }; }); app.directive('username', function($q, $timeout) { return { require: 'ngModel', link: function(scope, elm, attrs, ctrl) { var usernames = ['Jim', 'John', 'Jill', 'Jackie']; ctrl.$asyncValidators.username = function(modelValue, viewValue) { if (ctrl.$isEmpty(modelValue)) { // consider empty model valid return $q.resolve(); } var def = $q.defer(); $timeout(function() { // Mock a delayed response if (usernames.indexOf(modelValue) === -1) { // The username is available def.resolve(); } else { def.reject(); } }, 2000); return def.promise; }; } }; });</script></html>
修改内置验证
angularjs有一些内置的验证器,你可以很容易的移除或替换这些验证器。下面的例子演示了如何重写类型为email的验证器。
<!DOCTYPE html><html><head> <meta charset="uft-8"/> <title></title></head><script src="script/angular.min.js"></script><body ng-app="form-example-modify-validators"><form name="form" class="css-form" novalidate> <div> <label> Overwritten Email: <input type="email" ng-model="myEmail" overwrite-email name="overwrittenEmail" /> </label> <span ng-show="form.overwrittenEmail.$error.email">This email format is invalid!</span><br> Model: {{myEmail}} </div></form></body><script> var app = angular.module('form-example-modify-validators', []); app.directive('overwriteEmail', function() { var EMAIL_REGEXP = /^[a-z0-9!#$%&'*+/=?^_`{|}~.-]+@example\.com$/i; return { require: 'ngModel', link: function(scope, elm, attrs, ctrl) { // 指定了ngModel,并且输入类型为email if (ctrl && ctrl.$validators.email) { // 重写angularjs的内置email验证器 ctrl.$validators.email = function(modelValue) { return ctrl.$isEmpty(modelValue) || EMAIL_REGEXP.test(modelValue); }; } } }; });</script></html>
自定义form控件
angularjs实现了所有的html基本控件(input, select, textarea),大多数情况下足够用了,但是如果你需要更多的灵活性,你可以自定义form控件。
为了让自定义的控件可以支持ngModel,并且带有双向绑定,需要这样做:
- 实现$render方法,在NgModelController.$formatters之后它负责对数据进行渲染
- 在用户和控件交互之后,模型需要更新时,调用$setViewValue方法。通常是由DOM的事件监听器做的。
可以看下下面这个例子:
<!DOCTYPE html><html><head> <meta charset="uft-8"/> <title></title></head><script src="script/angular.min.js"></script><body ng-app="form-example2"><div contentEditable="true" ng-model="content" title="Click to edit">Some</div><pre>model = {{content}}</pre><style type="text/css"> div[contentEditable] { cursor: pointer; background-color: #D0D0D0; }</style></body><script> angular.module('form-example2', []).directive('contenteditable', function() { return { require: 'ngModel', link: function(scope, elm, attrs, ctrl) { // view -> model elm.on('blur', function() { ctrl.$setViewValue(elm.html()); }); // model -> view ctrl.$render = function() { elm.html(ctrl.$viewValue); }; // load init value from DOM ctrl.$setViewValue(elm.html()); } }; });</script></html>
如果我的文章对您有帮助,请用支付宝打赏:
- angular学习(十一)—— Form
- Angular学习(十一)——$watch、模板数据的展现
- JavaScript FAQ(十一)——表单(Form)
- angular学习总结十一——动态创建组件并实现交互二
- Angular—路由学习
- Angular学习(4)——Controller
- angular学习(一)—— 概览
- angular学习(三)—— Controller
- angular学习(四)—— Services
- angular学习(五)—— Scopes
- angular学习(七)—— Template
- angular学习(八)—— Expressions
- angular学习(九)—— Interpolation
- angular学习(十)—— Filter
- angular学习(十二)—— Directive
- angular学习(十三)——Component
- angular学习(十四)——Module
- angular学习(十五)——Provider
- 八皇后
- html页面滚动效果之marquee标签详解
- Eclipse出现异常,强制关闭后,无法打开闪退
- RxJava的使用与深入学习
- HNOI2012 矿场搭建
- angular学习(十一)—— Form
- 00 vim 简明教程
- 推荐系统Crab for Python2.7的搭建
- redis-3.0主从搭建实践
- 推荐系统Crab for Python2.7的搭建
- 根据圆心经纬度和半径计算地图zoom level
- 国王的烦恼 解题报告
- poj1011 && uva307 DFS + 剪枝
- 微信技术总监讲大数据高并发系统架构