28

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

$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 仍然无效。想法?

4

5 回答 5

15

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

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”,并将值设置为您要观看的对象集。

于 2013-09-08T16:46:17.380 回答
8

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

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

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

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

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

于 2013-09-08T10:47:02.477 回答
5

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

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

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

于 2015-11-10T22:24:46.003 回答
0

为了我的理智

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
    }
  });
于 2017-03-13T18:00:05.463 回答
0

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

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

是一个笨蛋。

于 2017-12-06T13:49:04.193 回答