百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

从浅入深剖析angular表单验证(ant 表单验证)

zhezhongyun 2025-05-02 14:46 42 浏览

最近手上维护的组件剩下的BUG都是表单验证,而且公司的表单验证那块代码经历的几代人,里面的逻辑开始变得不清晰,而且代码结构不是很angular。

是很有必要深入了解表单验证。

<body ng-controller="MainController">
    <form name="form" novalidate="novalidate">
        <input name="text" type="email" ng-model="name">
    </form>
</body>

ngModel是angular的黑魔法,实现双向绑定,当name的值变化的时候,input的value也会跟着变化。

当用户在input修改value的时候,name的值也会跟着变化。

novalidate="novalidate"的目的是去除系统自带的表单验证。

上面那段代码解析完,angular会在MainController的$scope下面生成一个变量"form",$scope.form,这个变量的名称跟html中form.name一致。

而$scope.form.text为文本输入框的Model,继承自ngModelController。

其中$scope.form实例自FormController。其内容为:

文本输入框的Model(也就是$scope.form.text)为:

其中$dirty/$pristine,$valid/$invalid,$error为常用属性。尤其是$error。

最简单的表单验证:

了解了form和输入框,就可以先撸个最简单的显示错误的指令。

html内容如下:

<form name="form" novalidate="novalidate">
     <input name="text" type="email" ng-model="name" error-tip>
</form>

指令代码如下:

    //    当输入框出错,就显示错误
    directive("errorTip",function($compile){
 return {
 restrict:"A",
 require:"ngModel",
 link:function($scope,$element,$attrs,$ngModel){
 //创建子scope
 var subScope = $scope.$new,
 //错误标签的字符串,有错误的时候,显示错误内容
 tip = '<span ng-if="hasError">{{errors | json}}</span>';
 //脏,而且无效,当然属于错误了
 $scope.hasError = function{
 return $ngModel.$invalid && $ngModel.$dirty;
 }
 //放回ngModel的错误内容,其实就是一个对象{email:true,xxx:true,xxxx:trie}
 $scope.errors = function{
 return $ngModel.$error;
 }
 //编译错误的指令,放到输入框后面
 $element.after($compile(tip)(subScope));
 }
 }
        });

先看看执行结果:

输入无效的邮箱地址的时候:

输入正确的邮箱地址的时候:

errorTip指令一开始通过 require:"ngModel" 获取ngModelController。然后创建用于显示错误的元素到输入框。

这里使用了$compile,$compile用于动态编译显示html内容的。具体原理可以看这里
:http://www.cnblogs.com/accordion/p/5156553.html.

当有错误内容的时候,错误的元素就会显示。

为什么subScope可以访问hasError和errors方法?

因为原型链。

自定义错误内容

好了,很明显现在的表单验证是不能投入使用的,我们必须自定义显示的错误内容,而且要显示的错误不仅仅只有一个。

显示多个错误使用ng-repeat即可,也就是把"errorTip"指令中的

tip = '<span ng-if="hasError">{{errors | json}}</span>';

改成:

tip = '<ul ng-if="hasError" ng-repeat="(errorKey,errorValue) in errors">' +
 '<span ng-if="errorValue">{{errorKey | errorFilter}}</span>' +
 '</ul>';

其中errorFilter是一个过滤器,用于自定义显示错误信息的。过滤器其实是个函数。

其代码如下:

        .filter("errorFilter",function{
 return function(input){
 var errorMessagesMap = {
 email:"请输入正确的邮箱地址",
 xxoo:"少儿不宜"
 }

 return errorMessagesMap[input];
 }
        });

结果如下:

好了,到这里就能够处理“简单”的表单验证了。对,简单的。我们还必须继续深入。

自定义表单验证!

那我们就来实现一个不能输入“帅哥”的表单验证吧。

指令如下:

        .directive("doNotInputHandsomeBoy",function($compile){
 return {
 restrict:"A",
 require:"ngModel",
 link:function($scope,$element,$attrs,$ngModel){
 $ngModel.$parsers.push(function(value){
 if(value === "帅哥"){
 //设置handsome为无效,设置它为无效之后,$error就会变成{handsome:true}
 $ngModel.$setValidity("handsome",false);
 }
 return value;
 })
 }
 }
        })

结果如下:

这里有两个关键的东西,$ngModel.$parsers和$ngModel.$setValidity.

$ngModel.$parsers是一个数组,当在输入框输入内容的时候,都会遍历并执行$parsers里面的函数。

$ngModel.$setValidity("handsome",false);设置handsome为无效,会设置$ngModel.$error["handsome"] = true;

也会设置delete $ngModel.$success["handsome"],具体可以翻翻源码。

这里我总结一下流程。

-->用户输入

-->angular执行所有$parsers中的函数

-->遇到$setValidity("xxoo",false);那么就会把xxoo当做一个key设置到$ngModel.$error["xxoo"]

-->然后errorTip指令会ng-repeat $ngModel.$error

-->errorFilter会对错误信息转义

-->最后显示错误的信息

自定义输入框的显示内容

很多时候开发,不是简简单单验证错误显示错误那么简单。有些时候我们要格式化输入框的内容。

例如,"1000"显示成"1,000"

"hello"显示成"Hello"

现在让我们实现自动首字母大写。

源码如下:

    <form name="form" novalidate="novalidate">
        <input name="text" type="text" ng-model="name" upper-case>
    </form>
        .directive("upperCase",function{
 return {
 restrict:"A",
 require:"ngModel",
 link:function($scope,$element,$attrs,$ngModel){
 $ngModel.$parsers.push(function(value){
 var viewValue;
 if(angular.isUndefined(value)){
 viewValue = "";
 }else{
 viewValue = "" + value;
 }

 viewValue = viewValue[0].toUpperCase + viewValue.substring(1);
 //设置界面内容
 $ngModel.$setViewValue(viewValue);
 //渲染到界面上,这个函数很重要
 $ngModel.$render;
 return value;
 })
 }
 }
        });

这里我们使用了$setViewValue和$render,$setViewValue设置viewValue为指定的值,$render把viewValue显示到界面上。

很多人以为使用了$setViewValue就能更新界面了,没有使用$render,最后不管怎么搞,界面都没刷新。

如果只使用了$ngModel.$parsers是不够的,$parsers只在用户在输入框输入新内容的时候触发,还有一种情况是需要重新刷新输入框的内容的:

那就是双向绑定,例如刚才的输入框绑定的是MainController中的$scope.name,当用户通过其他方式把$scope.name改成"hello",输入框中看不到首字母大写。

这时候就要使用$formatters,还是先看个例子吧.

<body ng-controller="MainController">
    <form name="form" novalidate="novalidate">
        <button ng-click="random">随机</button>
        <input name="text" type="text" ng-model="name" upper-case>
    </form>
</body>

MainController的内容:

    angular.module("app", [])
        .controller("MainController", function ($scope, $timeout) {
 $scope.random = function{
 $scope.name = "hello" + Math.random;
 }
        })

够简单吧,点击按钮的时候,$scope.name变成hello开头的随机内容.

很明显,hello的首字母没大写,不是我们想要的内容。

我们修改下指令的内容:

        .directive("upperCase",function{
 return {
 restrict:"A",
 require:"ngModel",
 link:function($scope,$element,$attrs,$ngModel){
 $ngModel.$parsers.push(function(value){
 var viewValue = upperCaseFirstWord(handleEmptyValue(value));
 //设置界面内容
 $ngModel.$setViewValue(viewValue);
 //渲染到界面上,这个函数很重要
 $ngModel.$render;
 return value;
 })
 //当过外部设置modelValue的时候,会自动调用$formatters里面函数
 $ngModel.$formatters.push(function(value){
 return upperCaseFirstWord(handleEmptyValue(value));
 })

 //防止undefined,把所有的内容转换成字符串
 function handleEmptyValue(value){
 return angular.isUndefined(value) ? "" : "" + value;
 }
 
 //首字母大写
 function upperCaseFirstWord(value){
 return value.length > 0 ? value[0].toUpperCase + value.substring(1) : "";
 }
 }
 }
        });

总结一下:

1.

-->用户在输入框输入内容

-->angular遍历$ngModel.$parsers里面的函数转换输入的内容,然后设置到$ngModel.$modelValue

-->在$ngModel.$parsers数组中的函数里,我们修改了$ngModel.$viewValue,然后$ngMode.$render渲染内容。

2.

-->通过按钮生成随机的字符串设置到name

-->每次脏检测都会判断name的值是否跟$ngModel.$modelValue不一致(这里是使用$watch实现的),不一致就反序遍历$formaters里面的所有函数并执行,把最终返回值赋值到$ngModel.$viewValue

-->刷新输入框内容

内容不完整的话,请拍砖,我继续加。

晚点补上源码剖析部分。

相关推荐

Python入门学习记录之一:变量_python怎么用变量

写这个,主要是对自己学习python知识的一个总结,也是加深自己的印象。变量(英文:variable),也叫标识符。在python中,变量的命名规则有以下三点:>变量名只能包含字母、数字和下划线...

python变量命名规则——来自小白的总结

python是一个动态编译类编程语言,所以程序在运行前不需要如C语言的先行编译动作,因此也只有在程序运行过程中才能发现程序的问题。基于此,python的变量就有一定的命名规范。python作为当前热门...

Python入门学习教程:第 2 章 变量与数据类型

2.1什么是变量?在编程中,变量就像一个存放数据的容器,它可以存储各种信息,并且这些信息可以被读取和修改。想象一下,变量就如同我们生活中的盒子,你可以把东西放进去,也可以随时拿出来看看,甚至可以换成...

绘制学术论文中的“三线表”具体指导

在科研过程中,大家用到最多的可能就是“三线表”。“三线表”,一般主要由三条横线构成,当然在变量名栏里也可以拆分单元格,出现更多的线。更重要的是,“三线表”也是一种数据记录规范,以“三线表”形式记录的数...

Python基础语法知识--变量和数据类型

学习Python中的变量和数据类型至关重要,因为它们构成了Python编程的基石。以下是帮助您了解Python中的变量和数据类型的分步指南:1.变量:变量在Python中用于存储数据值。它们充...

一文搞懂 Python 中的所有标点符号

反引号`无任何作用。传说Python3中它被移除是因为和单引号字符'太相似。波浪号~(按位取反符号)~被称为取反或补码运算符。它放在我们想要取反的对象前面。如果放在一个整数n...

Python变量类型和运算符_python中变量的含义

别再被小名词坑哭了:Python新手常犯的那些隐蔽错误,我用同事的真实bug拆给你看我记得有一次和同事张姐一起追查一个看似随机崩溃的脚本,最后发现罪魁祸首竟然是她把变量命名成了list。说实话...

从零开始:深入剖析 Spring Boot3 中配置文件的加载顺序

在当今的互联网软件开发领域,SpringBoot无疑是最为热门和广泛应用的框架之一。它以其强大的功能、便捷的开发体验,极大地提升了开发效率,成为众多开发者构建Web应用程序的首选。而在Spr...

Python中下划线 ‘_’ 的用法,你知道几种

Python中下划线()是一个有特殊含义和用途的符号,它可以用来表示以下几种情况:1在解释器中,下划线(_)表示上一个表达式的值,可以用来进行快速计算或测试。例如:>>>2+...

解锁Shell编程:变量_shell $变量

引言:开启Shell编程大门Shell作为用户与Linux内核之间的桥梁,为我们提供了强大的命令行交互方式。它不仅能执行简单的文件操作、进程管理,还能通过编写脚本实现复杂的自动化任务。无论是...

一文学会Python的变量命名规则!_python的变量命名有哪些要求

目录1.变量的命名原则3.内置函数尽量不要做变量4.删除变量和垃圾回收机制5.结语1.变量的命名原则①由英文字母、_(下划线)、或中文开头②变量名称只能由英文字母、数字、下画线或中文字所组成。③英文字...

更可靠的Rust-语法篇-区分语句/表达式,略览if/loop/while/for

src/main.rs://函数定义fnadd(a:i32,b:i32)->i32{a+b//末尾表达式}fnmain(){leta:i3...

C++第五课:变量的命名规则_c++中变量的命名规则

变量的命名不是想怎么起就怎么起的,而是有一套固定的规则的。具体规则:1.名字要合法:变量名必须是由字母、数字或下划线组成。例如:a,a1,a_1。2.开头不能是数字。例如:可以a1,但不能起1a。3....

Rust编程-核心篇-不安全编程_rust安全性

Unsafe的必要性Rust的所有权系统和类型系统为我们提供了强大的安全保障,但在某些情况下,我们需要突破这些限制来:与C代码交互实现底层系统编程优化性能关键代码实现某些编译器无法验证的安全操作Rus...

探秘 Python 内存管理:背后的神奇机制

在编程的世界里,内存管理就如同幕后的精密操控者,确保程序的高效运行。Python作为一种广泛使用的编程语言,其内存管理机制既巧妙又复杂,为开发者们提供了便利的同时,也展现了强大的底层控制能力。一、P...