自定义AngularJS表单验证方式

AngularJS做输入验证很方便,多数情况下只有当用户改变输入表单值时才进行输入验证。但我们的新需求是不仅改变时进行验证,而且在失去焦点时也要验证。先看看最终效果吧。一旦表单项验证不通过,将会在表单项四围显示红色错误边框,并且在其下方还会显示红色错误文字,甚至连表单项对应的标签也会显示成红色。想一想怎样来实现。

一、使用$touched属性

大家都知道,$dirty属性表示用户是否和表单项交互过(比如输入一些东西),只要有任何改变,该值为 true,与之相对的是 $pristine,因此只用这个属性只能做到改变控件值时进行输入验证。看这种效果:https://codepen.io/riafan/pen/KoPRqB

$touched属性表示用户是否访问过表单项,如果获得过焦点,在失去时该值为true,与之相对的是 $untouched。因此使用为个属性应该满足我们的新需求。看代码:https://codepen.io/riafan/pen/LdPVjO。

首先要控制错误文字的隐藏显示,使用表单项的$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()方法,通过递归可以访问过满足一定条件的当前元素最近父元素。有兴趣的可以关注一下。

相关代码:

发表评论

电子邮件地址不会被公开。 必填项已用*标注