Валидация не срабатывает, когда данные привязывают числовые значения min/max

У меня есть многочисленные поля ввода чисел, которые имеют значения атрибутов min и max, которые зависят от логики в другом месте моего приложения AngularJS, но при использовании привязок данных в этих атрибутах они больше не проверяются с помощью Angular, однако проверка HTML 5 все еще, кажется, забирает их.

var myApp = angular.module('myApp', []);
function FirstCtrl($scope) {
    $scope.min = 10;
    $scope.max = 20;
}
<div ng-app="myApp">
    <div ng-controller="FirstCtrl">
        <form name="myForm">
            <input type="number" name="one" required="required"
                   min="10" max="20" ng-model="one" />
            <input type="number" name="two" required="required"
                   min="{{ min }}" max="{{ max }}" ng-model="two" />
            <button ng-disabled="!myForm.$valid">Submit</button>
        </form>
    </div>
</div>

Текущая версия: http://jsfiddle.net/kriswillis/bPUVH/2/

Как вы можете видеть, проверка по-прежнему обрабатывается в HTML/CSS (Bootstrap), так как оба поля становятся красными, если они недействительны, однако кнопка отправки (обрабатываемая Angular) не отключается, когда второе поле инвалид. Кроме того, в myForm.two.$error нет минимальных и максимальных свойств, как в myForm.one.$error.

Кто-нибудь может увидеть, где я ошибаюсь?

Ответ 1

По-видимому, мы не можем использовать {{}} s (т.е. интерполяцию) для полей min и max. Я посмотрел исходный код и нашел следующее:

if (attr.min) {
  var min = parseFloat(attr.min);

$interpolate не вызывается, просто parseFloat, поэтому вам нужно указать строку, которая выглядит как число для min и max.

Ответ 2

Я написал директивы для заполнения пробела, ng-min и ng-max:

http://jsfiddle.net/g/s5gKC/

var app = angular.module('app', []);

function isEmpty(value) {
  return angular.isUndefined(value) || value === '' || value === null || value !== value;
}

app.directive('ngMin', function() {
    return {
        restrict: 'A',
        require: 'ngModel',
        link: function(scope, elem, attr, ctrl) {
            scope.$watch(attr.ngMin, function(){
                ctrl.$setViewValue(ctrl.$viewValue);
            });
            var minValidator = function(value) {
              var min = scope.$eval(attr.ngMin) || 0;
              if (!isEmpty(value) && value < min) {
                ctrl.$setValidity('ngMin', false);
                return undefined;
              } else {
                ctrl.$setValidity('ngMin', true);
                return value;
              }
            };

            ctrl.$parsers.push(minValidator);
            ctrl.$formatters.push(minValidator);
        }
    };
});

app.directive('ngMax', function() {
    return {
        restrict: 'A',
        require: 'ngModel',
        link: function(scope, elem, attr, ctrl) {
            scope.$watch(attr.ngMax, function(){
                ctrl.$setViewValue(ctrl.$viewValue);
            });
            var maxValidator = function(value) {
              var max = scope.$eval(attr.ngMax) || Infinity;
              if (!isEmpty(value) && value > max) {
                ctrl.$setValidity('ngMax', false);
                return undefined;
              } else {
                ctrl.$setValidity('ngMax', true);
                return value;
              }
            };

            ctrl.$parsers.push(maxValidator);
            ctrl.$formatters.push(maxValidator);
        }
    };
});

angular.bootstrap(document.body, ['app']);

Ответ 3

Angular теперь поддерживает ng-min и ng-max из коробки без необходимости писать собственные директивы. См. этот скрипт, который использует Angular 1.4.2.

Ответ 4

Я также написал для него настраиваемую директиву:

Рабочая демонстрация: http://plnkr.co/edit/BJ98ZR?p=preview

.directive('validateRange', ['$parse', function($parse) {

    function link($scope, $element, $attrs, ngModel) {
        var attrRange, range = [];

        function validate(value) {
            var validMin = true, validMax = true;
            if (typeof range[0] === 'number') {
                ngModel.$setValidity('min', value >= range[0]);
                validMin = value >= range[0];
            }
            if (typeof range[1] === 'number') {
                ngModel.$setValidity('max', value <= range[1]);
                validMax = value <= range[1];
            }
            return validMin && validMax ? value : undefined;
        }

        attrRange = $attrs.validateRange.split(/,/);

        range[0] = $parse(attrRange[0] || '')($scope);
        range[1] = $parse(attrRange[1] || '')($scope);

        $scope.$watchCollection('[' + $attrs.validateRange + ']', function(values) {
            range = values;
            validate(ngModel.$viewValue);
        });

        ngModel.$parsers.unshift(validate);
        ngModel.$formatters.unshift(validate);
    }

    return {
        link: link,
        require: 'ngModel'
    };

}]);

Пример использования:

<form name="myform" ng-init="minvalue=0;value=1;maxvalue=2">
    Min Value:
    <input type="number" name="minvalue" required
           data-validate-range="0,value"
           ng-model="minvalue">

    Value:
    <input type="number" name="value" required
           data-validate-range="minvalue,maxvalue"
           ng-model="value">

    Max Value:
    <input type="number" name="maxvalue" required
           data-validate-range="value,false"
           ng-model="maxvalue">

    <pre>
        myform.minvalue.$error {{ myform.minvalue.$error }}
        myform.value.$error    {{ myform.value.$error }}
        myform.maxvalue.$error {{ myform.maxvalue.$error }}
    </pre>
</form>

Ответ 5

У меня одна и та же проблема. получил успех, используя эту директиву:

angular.module('tech').directive('maximum', ['$parse',
  function($parse) {
    return {
      restrict: 'A',
      require: '?ngModel',

      link: function(scope, element, attrs, ctrl) {

        if (!ctrl) return;
        attrs.maximum = true;


        var checkMax = function(n) {
          if (!n) return;

          var actualValue = ctrl.$viewValue;
          var maxValue = attrs.maximum;
          if (parseInt(actualValue) > parseInt(maxValue)) {
            ctrl.$setValidity('maximum', false);
          } else {
            ctrl.$setValidity('maximum', true);
          }
        };
        scope.$watch(attrs.ngModel, checkMax);
        //attrs.$observe('ngModel', remind);
        attrs.$observe('maximum', checkMax);

      }
    }
  }
]);

Я получил это решение из этого веб-сайта. для всего кода вы можете перейти этот веб-сайт