角度:验证多个依赖字段

angular: Validate multiple dependent fields

本文关键字:依赖 字段 验证 角度      更新时间:2023-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的好来源。

编辑:我想出了以下自定义指令("共享"是原始代码"百分比"的同义词)。共享验证指令采用形式"{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 仍然无效。想法?

好的,以下工作(同样,"份额"是"百分比"):

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 中,将属性设置为"共享验证",并将值设置为要监视的对象集。

你可以检查angularui库(ui-utility部分)。它有 ui 验证指令。

那么可以实现它的一种方法是

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

在控制器上创建基于帐户计算返回 true 或 false 的方法checkOverflow

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

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

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

这很有效,因为验证不是由特定的输入字段决定的,而是由我的输入总数决定的。 如果您需要执行由多个字段的组合确定的验证,则相同的方法可能有效。

为了我的理智

.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
    }
  });

您可以定义仅负责此检查的单个指令。

<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);
        }
      };
    }
  };
});

这是一个笨蛋。