自定义 AngularJS 表单验证方式

自定义 AngularJS 表单验证方式

Flying
2015-02-20 / 0 评论 / 80 阅读 / 正在检测是否收录...

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

check-angular.svg

使用 $touched 属性

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

changing-validation.png

具体实现可参考改变值时验证的代码。

$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 查看示例代码及效果

5

评论 (0)

取消