zoukankan      html  css  js  c++  java
  • AngularJS 1.2.x 学习笔记(表单校验篇)

    https://my.oschina.net/cokolin/blog/526911

    摘要: 本文首发于 blog.csdn.net/vipshop_ebs/article/details/39472873 作者为本人,只是该博客暂停维护转到 OSC 了。 本文主要讲 AngularJS 的表单验证,Demo页面我使用 Bootstrap3.2.0 作为前端显示框架,同时引入了jQuery1.11.1,AngularJS 为刚刚新发布的1.2.25。

    在新的项目中巧妙地接触到了Google提供的前端MVC框架 AngularJS,它的数据双向绑定真的能够让前端开发者从繁复的DOM操作中解放出来。

    在我断断续续的学习AngularJS一个多月后,可以来讲讲我的 AngularJS 学习笔记了,当前首先要讲的是前端必不可少的元素——表单,以下用ng代替AngularJS,本文章适合有一定ng基础的同学。

    Demo页面我使用Bootstrap3.2.0作为前端显示框架,同时引入了jQuery1.11.1,AngularJS 为刚刚新发布的1.2.25(使用1.3.0-rc2出现了一个奇怪的BUG,看来还是稳定版稳定),首先来一个大而全的表单页面:

    因为整篇文章的字数限制,移到另一篇博客了:
    http://my.oschina.net/cokolin/blog/526910

    请忽略这个表单字段组合的合理性^_^。表单中,为所有字段绑定了ngModel,设置了校验指令,例如required,ngMinlength,ngMaxlength等,同时利用了ngShow、ngClass这两个指令来实现校验错误信息的提示。

    在ngApp内定义一个form,实际上表示这个form已经被ng接管了,在相关的 ngController 的 $scope 域中会自动生成与这个form验证有关的字段,但是需要注意:

    1. 给 form 设置 name 属性才能使用ng的表单校验,可能设置了name,ng才会接管这个form;

    2. 给 form 设置 novalidate 属性是关闭浏览器内置的校验器,各个浏览器内置的校验器功能不一,也不够ng丰富,所以还是关了比较好;

    3. 避免给 form 设置 action 属性,设置了可能会导致表单不经校验直接提交的结果,当然也可以用其他方案解决这个问题;

    4. 给 form 设置 autocomplete="off" 是关闭浏览器的自动填写功能,这个功能有时候连按钮的disabled状态也记住了,可能会导致避免表单重复提交策略上的问题。

    页面或CSS中可以通过 formName.inputFieldName.property 来访问表单字段的验证状态,主要有这几种状态:

    1. formName.inputFieldName.$pristine,Boolean类型,表单字段是否未修改;

    2. formName.inputFieldName.$dirty,Boolean类型,表单字段是否已修改;

    3. formName.inputFieldName.$valid,Boolean类型,表单字段是否验证通过;

    4. formName.inputFieldName.$invalid,Boolean类型,表单字段是否未通过验证;

    5. formName.inputFieldName.$error,Object类型,存储表单字段验证项的通过与否,例如
      formName.inputFieldName.$error.required,这个Boolean类型表示表单字段是否未通过必填的验证;

    以上是默认的表单字段的验证状态,存储在$scope域中,表单自身也有这几个状态:

    1. formName.$pristine

    2. formName.$dirty

    3. formName.$valid

    4. formName.$invalid

    ng会给表单元素加上一些校验相关的css,表单字段会根据验证状态添加这几个class:

    • .ng-valid

    • .ng-invalid

    • .ng-pristine

    • .ng-dirty

    可以设置这些css的属性改变表单域的样式,例如设置:input.ng-invalid {border-color:red;},把未验证通过的表单元素边框设置为红色。

    ng支持大部分的的HTML5表单类型,具体请查看ng的文档。表单中需要注意的几点:

    1. 如果在input绑定了ng-model,点击表单上的reset按钮并不会重置ng-model中绑定的数据,所以建议给reset按钮添加ng-click事件实现表单的重置;

    2. select 元素如果绑定了ng-model且无空值的option,那么ng会自动给select添加一个空option,在用户选择select的值以后,这个空 option会自动消失,所以为了显示方便,建议给相关的select添加一个value属性为空的option,例 如<optionvalue=""></option>,或者使用ng-init给绑定的ng-model初始化一个值;

    3. checkbox 或radio在用户点击后会优先执行浏览器给定的状态,所以可能会出现设置ng-checked失效的情况,例如给checkbox设置ng- checked="selectCount ==10",如果 $scope.selectCount 永远不等于10,但用户手动去点击这个checkbox,依然可以使这个checkbox处于选中状态,不过如果用户操作其他元素使 $scope.selectCount 的值改变还是会导致checkbox的状态变化的,我当前暂时未发现更优雅的解决方案,主要是给该checkbox添加ng-click事件,重复了 ng-checked 的检查代码,然后通过DOM操作重新设置了checkbox的状态;

    表单字段的验证也可以自定义,在Demo的表单中,我自定义了一个重复密码校验的指令(my-pwd-match="myForm.password"):

    myApp.directive('myPwdMatch', [function(){  
        return {  
            restrict: "A",  
            require: 'ngModel',  
            link: function(scope,element,attrs,ctrl){  
                var tageCtrl = scope.$eval(attrs.myPwdMatch);  
                tageCtrl.$parsers.push(function(viewValue){  
                    ctrl.$setValidity('pwdmatch', viewValue == ctrl.$viewValue);  
                    return viewValue;  
                });  
                ctrl.$parsers.push(function(viewValue){  
                    if(viewValue == tageCtrl.$viewValue){  
                        ctrl.$setValidity('pwdmatch', true);  
                        return viewValue;  
                    } else{  
                        ctrl.$setValidity('pwdmatch', false);  
                        return undefined;  
                    }  
                });  
            }  
        };  
    }]);

    该自定义指令具体作用是:同时监听“用户密码”和”重复密码“两个字段的变化,根据用户的输入值的异同设置”重复密码“验证状态:

    myForm.rpassword.$error.pwdmatch;

    注意:重复密码输入框必须绑定ngModel才能使用这个指令。在ng的1.3.0-rc2版本上我定义这个指令在使用时发现ng无故给“重复密码”设置了一个验证状态:

    myForm.rpassword.$error.parse;

    暂不清楚这个是 BUG 还是1.2和1.3两个版本之间指令实现的差异。

    上面这个是实时的本地验证,如果需要要实现字段的远端验证,一般验证的频率不会那么频繁,建议添加一些触发条件:

    • 当字段值长度等于多少时执行远端验证(这个比较适合图片验证码);

    • 延迟验证,就是等一两秒数据如果没有变化再执行验证;

    • 输入框丢失焦点后再触发验证(适合大部分输入框);

    • 用户点击提交按钮后再触发验证。

    开发者可以使用这些远端验证中的一种或几种的组合,在确定输入框是否处于丢失焦点状态可以使用如下的指令:

    myApp.directive('myFocusValid', [function(){  
        return {  
            restrict: "A",  
            require: 'ngModel',  
            link: function(scope,element,attrs,ctrl){  
                ctrl.$focused = false;  
                ctrl.$blured = true;  
                element.bind("focus", function(evt){  
                    scope.$apply(function(){  
                        ctrl.$focused = true;  
                        ctrl.$blured = false;  
                    });  
                }).bind("blur", function(evt){  
                    scope.$apply(function(){  
                        ctrl.$focused = false;  
                        ctrl.$blured = true;  
                    });  
                });  
            }  
        };  
    }]);

    该指令是给输入框校验器添加两个状态:$focused 和 $blured,为input添加 my-focus-valid 属性即可以使用这个指令。

    不过我还是喜欢在用户点击按钮以后再使用远端验证,因为这样做可以避免在js中写复杂的阻止表单提交的逻辑,这个表单我使用ngSubmit指令,详细的实现请看controller:

    myApp.controller("myCtrl", ["$scope", "$http", function($scope,$http){  
        console.log($scope)  
          
        $scope.safeTypes = [{  
            value: 0,  
            text: "不保存账户状态"  
        }, {  
            value: 30,  
            text: "保存半个小时"  
        }, {  
            value: 60,  
            text: "保存一个小时"  
        }, {  
            value: 180,  
            text: "保存三个小时"  
        }, {  
            value: 60 * 24,  
            text: "保存一天"  
        }, {  
            value: 60 * 24 * 7,  
            text: "保存一周"  
        }, {  
            value: 60 * 24 * 30,  
            text: "保存一个月"  
        }];  
      
        $scope.$watch("formArgs.username", function(newVal,oldVal){  
            var ctrl = $scope.myForm.username;  
            var usedNames = ctrl.$usedNames;  
            if(usedNames && usedNames[newVal]){  
                ctrl.$setValidity('remoted', false);  
            } else{  
                ctrl.$setValidity('remoted', true);  
            }  
        });  
      
        $scope.doSubmit = function(){  
            var username = $scope.formArgs ? $scope.formArgs.username : undefined;  
            var ctrl = $scope.myForm.username;  
            if(username){  
                $http({  
                    method: 'POST',  
                    url: 'json/check-username.json',  
                    data: {  
                        username: username  
                    }  
                }).success(function(resp){  
                    if(resp.status != "success"){  
                        ctrl.$setValidity('remoted', false);  
                        if(ctrl.$usedNames){  
                            ctrl.$usedNames[username] = true;  
                        } else{  
                            var obj = {};  
                            obj[username] = true;  
                            ctrl.$usedNames = obj;  
                        }  
                    } else{  
                        ctrl.$setValidity('remoted', true);  
                    }  
                    if($scope.myForm.$valid){  
                        alert("提交表单数据");  
                    }  
                }).error(function(){  
                    ctrl.$setValidity('remoted', false);  
                });  
            } else{  
                ctrl.$setValidity('remoted', true);  
            }  
            $scope.myForm.submitted = true;  
        }  
    }]);

    doSubmit事件中,我先判断用户名是否可以已被使用,然后再真正的执行表单的提交动作,同时也无论验证状态如何,都会设置表单的 submitted 状态为 true,让校验的结果能够在页面上快速的显示出来。

    ng的1.2版本支持的表单元素主要是input、select、button、textarea, 其中input的type主要包括:text、hidden、password、checkbox、radio、file、image、reset、 submit和扩展的email、url、number,1.3版在1.2版的基础上又扩展了一些字段,包括:

    1. dateTimeLocal:yyyy-MM-dd'T'HH:mm:ss;

    2. date:yyyy-MM-dd;

    3. month:yyyy-MM;

    4. time:HH:mm:ss;

    5. week:yyyy-W##;

    这些日期类型,所有的日期字段默认都是ISO的标准样式,但跟国内的习惯还是有一些差异,例如 dateTimeLocal中间多了一个字母T,week表示该年的第几周,例如2014-W49,这些日期类型的加入大大的增强了ng的功能,希望 1.3版本能够尽快的实现stable,不过不知道ng是否支持<input type="range">这个值域类型呢?

    表单验证的效果图:

    项目工程文件:

    http://download.csdn.net/download/cackling/7954867

    以后有时间会提供一下 git 项目地址。

    作者:cackling

  • 相关阅读:
    LeetCode Binary Tree Inorder Traversal
    LeetCode Populating Next Right Pointers in Each Node
    LeetCode Construct Binary Tree from Inorder and Postorder Traversal
    LeetCode Reverse Linked List II
    LeetCode Populating Next Right Pointers in Each Node II
    LeetCode Pascal's Triangle
    Palindrome Construct Binary Tree from Preorder and Inorder Traversal
    Pascal's Triangle II
    LeetCode Word Ladder
    LeetCode Binary Tree Zigzag Level Order Traversal
  • 原文地址:https://www.cnblogs.com/danghuijian/p/6006846.html
Copyright © 2011-2022 走看看