AngularJS 做输入验证很方便,多数情况下只有当用户改变输入表单值时才进行输入验证。但我们的新需求是不仅改变时进行验证,而且在失去焦点时也要验证。一旦表单项验证不通过,将会在表单项四围显示红色错误边框,并且在其下方还会显示红色错误文字,甚至连表单项对应的标签也会显示成红色。想一想怎样来实现。
使用 $touched 属性
大家都知道,$dirty
属性表示用户是否和表单项交互过(比如输入一些东西),只要值有任何改变,该值为 true
,与之相对的是 $pristine
,因此只用这个属性只能做到改变控件值时进行输入验证。看最终效果如下图:
具体实现可参考改变值时验证的代码。
$touched
属性表示用户是否访问过表单项,如果获得过焦点,在失去时该值为 true
,与之相对的是 $untouched
。因此使用为个属性应该满足我们的新需求。具体实现可参考失去焦点时验证的代码 。
首先要控制错误文字的隐藏显示,使用表单项的 $touched
和 $error
属性就很容易做到了。接下来要显示红色边框及标签,使用表单项的 $touched 和 $invalid
属性动态添加 has-error
样式就实现了。关键问题是这个样式应该设置在哪里,是对标签和表单项都设置吗?不是。那样会有多个表达式,造成代码混乱。在该实例中,我们将其设置在其直接父容器(form-group)上,设置该容器的颜色,下面的子元素都能从父容器继承颜色。
使用自定义指令
值得注意的是,$touched 属性是在 AngularJS 1.3 以上的版本才引入的。不巧的是,我们的项目用的是 AngularJS 1.2。是不是很蛋痛?所谓“车到山前必有路”,我们要捕获失去焦点事件,还要动态添加样式来显示错误提示。是不是在 Controller 中添加状态变量变可以了?如果只有一个表单项,这样做无可厚非。但如果要处理多个输入验证这种方式就不可取了。 推荐的做法是为表单项添加自定义指令来完成同样的功能。具体实现可参考如下:
angular.module('formApp', [])
.directive('blurValid', function() {
var FORM_GROUP_CLASS = 'form-group';
var ERROR_CLASS = 'has-error';
/**
* Find the nearest form with recursion
* @param el
* @returns {*}
*/
function findClosestForm(el) {
var el = angular.element(el);
if (el.parent().attr('novalidate') !== undefined) {
return el.parent()[0];
} else {
return findClosestForm(el.parent());
}
}
/**
* Find the nearest form group with recursion
* @param el
* @returns {*}
*/
function findClosestFormGroup(el) {
var el = angular.element(el);
if (el.parent().hasClass(FORM_GROUP_CLASS)) {
return el.parent();
} else {
return findClosestFormGroup(el.parent());
}
}
return {
require: 'ngModel',
restrict: 'AE',
link: function($scope, el, attr, ctrl) {
el.on('blur keyup', function(event) {
var group = findClosestFormGroup(el);
if (ctrl.$invalid) {
group.addClass(ERROR_CLASS);
} else {
group.removeClass(ERROR_CLASS);
}
});
$scope.reset = function(event) {
var form = findClosestForm(event.target);
angular.element(form.querySelectorAll('.' + FORM_GROUP_CLASS)).removeClass(ERROR_CLASS);
};
}
};
})
上面代码,我们自定义了指令 blurValid。由于要访问 NgModelController,所有加入了 ngModel 依赖。关键点在于通过监听 blur、 keyup 事件,检验表单项对应的控制器是否验证验证通过,通过就在表单项的父容器上添加错误样式 has-error,通过就移除。
当然 AngularJS 中要用 jqLite 查找元素并不简单。find() 方法只支持通过标签来查找,父级的父级(...) 用多个 parent() 方法又太耦合了。该实例中我们用的是递归查找。如 findClosestFormGroup()、findClosestForm() 方法,通过递归可以访问过满足一定条件的当前元素最近父元素。有兴趣的可以关注一下。
参考示例
访问 codepen 查看示例代码及效果
评论 (0)