【问题标题】:angular: Validate multiple dependent fields角度:验证多个依赖字段
【发布时间】:2013-09-12 00:09:26
【问题描述】:

假设我有以下(非常简单的)数据结构:

$scope.accounts = [{
   percent: 30,
   name: "Checking"},
 { percent: 70,
   name: "Savings"}];

然后我将以下结构作为表单的一部分:

<div ng-repeat="account in accounts">
    <input type="number" max="100" min="0" ng-model="account.percent" />
    <input type="text" ng-model="account.name" />
</div>

现在,我想验证每组帐户的百分比总和是否为 100,但我看到的大多数自定义指令示例只处理验证单个值。创建将同时验证多个依赖字段的指令的惯用方法是什么?在 jquery 中有相当多的解决方案,但我一直无法找到 Angular 的良好来源。

编辑:我想出了以下自定义指令(“share”是原始代码“percent”的同义词)。 share-validate 指令将"{group: accounts, id: $index}" 形式的映射作为其值。

app.directive('shareValidate', function() {
return {
    restrict: 'A',
    require: 'ngModel',
    link: function(scope, elem, attr, ctrl) {
        ctrl.$parsers.unshift(function(viewValue) {
            params = angular.copy(scope.$eval(attr.shareValidate));
            params.group.splice(params.id, 1);
            var sum = +viewValue;
            angular.forEach(params.group, function(entity, index) {
                sum += +(entity.share);
            });
            ctrl.$setValidity('share', sum === 100);
            return viewValue;
        });
    }
};
});

这几乎可以工作,但无法处理字段无效的情况,但随后对另一个字段的更改使其再次有效。例如:

Field 1: 61
Field 2: 52

如果我将字段 2 降低到 39,字段 2 现在将有效,但字段 1 仍然无效。想法?

【问题讨论】:

    标签: javascript validation angularjs


    【解决方案1】:

    好的,以下工作(同样,“分享”是“百分比”):

    app.directive('shareValidate', function () {
    return {
        restrict: 'A',
        require: 'ngModel',
        link: function(scope, elem, attr, ctrl) {
            scope.$watch(attr.shareValidate, function(newArr, oldArr) {
                var sum = 0;
                angular.forEach(newArr, function(entity, i) {
                    sum += entity.share;
                });
                if (sum === 100) {
                    ctrl.$setValidity('share', true);
                    scope.path.offers.invalidShares = false;
                }
                else {
                    ctrl.$setValidity('share', false);
                    scope.path.offers.invalidShares = true;
                }
            }, true); //enable deep dirty checking
        }
    };
    });
    

    在 HTML 中,将属性设置为“share-validate”,并将值设置为要观看的对象集。

    【讨论】:

    • 不错的可行解决方案,但我会说它可以改进以使其更通用。我认为在指令内硬编码范围路径不是一个好习惯。我做了一些更通用的事情(也许将来会对你有所帮助):liviutrifoi.wordpress.com/2013/10/19/…
    • 嗨,你能分享一下上面例子的工作小提琴吗,谢谢
    【解决方案2】:

    您可以查看 angularui 库(ui-utility 部分)。它有 ui-validate 指令。

    你可以实现它的一种方法是

    <input type="number" name="accountNo" ng-model="account.percent"
    ui-validate="{overflow : 'checkOverflow($value,account)' }">
    

    在控制器上创建方法checkOverflow,根据帐户计算返回真或假。

    我自己没有尝试过,但想分享这个想法。也请阅读网站上的示例。

    【讨论】:

    • 好的,我去看看。也对如何构建我自己的自定义指令来解决这个问题感兴趣。
    • 如果您构建自己的指令,您可以查看开发者指南中的Custom Validation 部分docs.angularjs.org/guide/forms
    • 我认为 ui-validate 在这里不起作用,因为它也只验证编辑的字段。因此,如果您输入例如两个字段都为 20,它们都是无效的。如果您随后将其中一个更改为 80,则该字段确实有效,但另一个字段仍然无效。
    【解决方案3】:

    我有一个动态表单,我的表单上可以有可变数量的输入字段,我需要限制要添加的输入控件的数量。

    我不能轻易限制这些输入字段的添加,因为它们是由其他因素组合生成的,所以如果输入字段的数量超过限制,我需要使表单无效。我通过在我的控制器ctrl.myForm 中创建对表单的引用来做到这一点,然后每次动态生成输入控件时(在我的控制器代码中),我会进行限制检查,然后像这样在表单上设置有效性:ctrl.myForm.$setValidity("maxCount", false);

    这很有效,因为验证不是由特定的输入字段决定的,而是我输入的总数。如果您需要由多个字段的组合确定需要完成的验证,那么同样的方法也可以工作。

    【讨论】:

      【解决方案4】:

      为了我的理智

      HTML

      <form ng-submit="applyDefaultDays()" name="daysForm" ng-controller="DaysCtrl">
      <div class="form-group">
          <label for="startDate">Start Date</label>
          <div class="input-group">
              <input id="startDate"
                     ng-change="runAllValidators()"
                     ng-model="startDate"
                     type="text"
                     class="form-control"
                     name="startDate"
                     placeholder="mm/dd/yyyy"
                     ng-required
              />
          </div>
      </div>
      <div class="form-group">
          <label for="eEndDate">End Date</label>
          <div class="input-group">
              <input id="endDate"
                     ng-change="runAllValidators()"
                     ng-model="endDate"
                     type="text"
                     class="form-control"
                     name="endDate"
                     placeholder="mm/dd/yyyy"
                     ng-required
              />
          </div>
      </div>
      <div class="text-right">
          <button ng-disabled="daysForm.$invalid" type="submit" class="btn btn-default">Apply Default Dates</button>
      </div>
      

      JS

      'use strict';
      
      angular.module('myModule')
        .controller('DaysCtrl', function($scope, $timeout) {
          $scope.initDate = new Date();
          $scope.startDate = angular.copy($scope.initDate);
          $scope.endDate = angular.copy($scope.startDate);
          $scope.endDate.setTime($scope.endDate.getTime() + 6*24*60*60*1000);
      
          $scope.$watch("daysForm", function(){
            //fields are only populated after controller is initialized
            $timeout(function(){
              //not all viewalues are set yet for somereason, timeout needed
              $scope.daysForm.startDate.$validators.checkAgainst = function(){
                $scope.daysForm.startDate.$setDirty();
                return (new Date($scope.daysForm.startDate.$viewValue)).getTime() <=
                  (new Date($scope.daysForm.endDate.$viewValue)).getTime();
              };
      
              $scope.daysForm.endDate.$validators.checkAgainst = function(){
                $scope.daysForm.endDate.$setDirty();
                return (new Date($scope.daysForm.startDate.$viewValue)).getTime() <=
                  (new Date($scope.daysForm.endDate.$viewValue)).getTime();
              };
            });
          });
      
          $scope.runAllValidators = function(){
            //need to run all validators on change
            $scope.daysForm.startDate.$validate();
            $scope.daysForm.endDate.$validate();
          };
      
          $scope.applyDefaultDays = function(){
              //do stuff
          }
        });
      

      【讨论】:

        【解决方案5】:

        您可以定义一个只负责此检查的指令。

        <form>
          <div ng-repeat="account in accounts">
            <input type="number" max="100" min="0" ng-model="account.percent" />
            <input type="text" ng-model="account.name" />
          </div>
          <!-- HERE IT IS -->
          <sum-up-to-hundred accounts="accounts"></sum-up-to-hundred>
        </form>
        

        这是简单指令的代码。

        app.directive('sumUpToHundred', function() {
          return {
            scope: {
              accounts: '<'
            },
            require: {
              formCtrl: '^form'
            },
            bindToController: true,
            controllerAs: '$ctrl',
            controller: function() {
              var vm = this;
        
              vm.$doCheck = function(changes) {
                var sum = vm.accounts.map((a)=> a.percent).reduce((total, n)=> total + n);
                if (sum !== 100) {
                  vm.formCtrl.$setValidity('sumuptohundred', false);
                } else {
                  vm.formCtrl.$setValidity('sumuptohundred', true);
                }
              };
            }
          };
        });
        

        Here 是个笨蛋。

        【讨论】:

          猜你喜欢
          • 2016-05-27
          • 2011-04-27
          • 1970-01-01
          • 2015-10-25
          • 2011-01-01
          • 1970-01-01
          • 2012-09-13
          • 2013-12-26
          • 1970-01-01
          相关资源
          最近更新 更多