【问题标题】:Keeping Angular form validation DRY保持 Angular 表单验证 DRY
【发布时间】:2015-06-15 22:05:18
【问题描述】:

我有一个可能包含 15-20 个字段的表单。它们中的每一个都包含一个看起来像这样的属性:

ng-class='addressForm.city.$invalid && addressForm.city.$touched ? "error" : ""'

所以我将那长串代码重复了 15-20 次。这让我的 DRY 警钟大放异彩。

我可以设计自己的方法来使这个更干燥,但我不想重新发明轮子。是否有一种普遍接受的方式来保持 Angular 表单验证 DRY?

【问题讨论】:

  • 你不能用某种帮手把它包起来吗?
  • 您可以在提交事件中检查无效字段,然后您可以将其更改为ng-class="{'error': addressForm[field-name-here].invalid }"
  • @DaveNewton 我想这是一种可能性。接受有关如何进行的建议。
  • @TanaseButcaru 似乎是一种改进,虽然没有我希望的那么大。
  • 如果你想让它“实时”(在用户更改值后立即检查字段有效性),你可以添加ng-change="fieldChanged(field-name-or-id)。此外,ng-class 属性应该在那里。这与我的第一个解决方案之间的唯一区别是您在值更改时进行验证,而不是在提交时进行验证。如果要删除ng-class,只需将this 传递给fieldChanged 函数(这将是元素的实例)并通过angular.element 添加/删除类

标签: angularjs validation dry


【解决方案1】:

如果只是为了样式使用 CSS。

Angular 会将.ng-invalid.ng-touched 添加到无效和触摸的输入元素中。

或者您可以将整个内容包装在类似

的指令中
angular.module('module').directive('errorClass', function(){
    return{
        require: 'ngModel',
        link: function(scope, el, attr, model) {
             function setClass() {
                if(model.$touched && model.$invalid) {
                   if(!el.hasClass('error')) { 
                      el.addClass('error');
                   }
                } else {
                    el.removeClass('error');
                }
             }

             scope.$watch(function(){ return model.$touched; }, setClass);
             scope.$watch(function(){ return model.$invalid; }, setClass);
        }
    }
});

而且我还没有实际使用过这个指令,所以它可能需要一些调整。

【讨论】:

  • 你的范围是什么。$watches 在看?
  • 手表可以将函数作为其第一个参数而不是字符串。在这种情况下,他们正在观察模型是否被触摸或无效。
【解决方案2】:

正如@lain 所说,如果字段无效,您不必添加另一个类(如error),Angular 默认为您添加,只是名称不同(ng-invalid)。 你可以看到它是如何使用here(来自Angular的官方表单示例)。

如果你仍然想以你的方式这样做,这是我最新评论的实现,使用 ngChange 指令。

html:

<input type="text" ng-model="addressForm.city" required ng-change="fieldChanged(this, 'city')">

变化事件:

$scope.fieldChanged = function(el, fieldName){
   if($scope.addressForm[fieldName].$invalid && $scope.addressForm[fieldName].$touched) angular.element(el).addClass('error');
   else angular.element(el).removeClass('error');
}

这不是好的做法(在控制器中操作 DOM),您应该在指令中实现它,但是将指令绑定到每个字段会添加观察者,我个人尽量避免使用许多观察者。

更优雅的选择是将 ngChangengClass 结合起来,或者简单地在控制器中进行简单的 DOM 操作。这是你的选择:)

【讨论】:

    【解决方案3】:

    我最终为此创建了自己的指令。我相信以下指令在应用时将与此等效:

    form(name='addressForm')
      input(
        type='text'
        name='city'
        ng-class='addressForm.city.$invalid && (addressForm.city.$touched || addressForm.$submitted) ? "error" : ""'
      )
    

    除此之外,我可以这样做:

    form(name='addressForm')
      input(
        type='text'
        name='city'
        validate-for='addressForm'
      )
    

    该指令将检查有效性:

    • 模糊
    • 表单提交
    • 值变化

    这是代码(ES6):

    'use strict';
    
    class ValidateFor {
      constructor() {
        this.restrict = 'A';
        this.require = 'ngModel';
    
        this.link = ($scope, $element, $attrs, ngModel) => {
          var form = $scope[$attrs.validateFor];
          var field = form[$element.attr('name')];
    
          $scope.$on('form-submitted', () => {
            this.checkForErrors(field, $element);
          });
    
          $scope.$watch(() => ngModel.$modelValue, () => {
            if (field.$touched) {
              this.checkForErrors(field, $element);
            }
          });
    
          $element.bind('blur', () => {
            this.checkForErrors(field, $element);
          });
        };
      }
    
      checkForErrors(field, $element) {
        if (field.$invalid) {
          $element.addClass('error');
        } else {
          $element.removeClass('error');
        }
      }
    }
    
    ValidateFor.$inject = [];
    

    您甚至可以消除在validate-for 中提供表单名称的必要性。我之所以这样做,是因为我有一些嵌套形式的情况。

    【讨论】:

    • 嗨,这太棒了,你有更新 Angular 8 的代码吗?
    【解决方案4】:

    valdr 看起来很棒。我还没用过,但我会试试看,稍后会更新这篇文章。

    【讨论】:

      猜你喜欢
      • 2011-06-06
      • 1970-01-01
      • 1970-01-01
      • 2021-09-22
      • 1970-01-01
      • 2018-09-26
      • 1970-01-01
      • 2018-02-08
      • 1970-01-01
      相关资源
      最近更新 更多