Kris
Kris

Reputation: 1094

Validation not triggered when data binding a number input's min / max attributes

I have numerous number input fields that have min and max attribute values that depend on logic elsewhere in my AngularJS app, but when using data bindings within these attributes they are no longer validated by Angular, however, the HTML 5 validation still appears to pick them up.

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>

Live version: http://jsfiddle.net/kriswillis/bPUVH/2/

As you can see, the validation is still handled fine in the HTML/CSS (Bootstrap) as both fields turn red when invalid, however, the submit button (handled by Angular) is not disabled when the second field is invalid. Also, there are no min and max properties in myForm.two.$error as there are in myForm.one.$error.

Can anyone see where I'm going wrong?

Upvotes: 11

Views: 16118

Answers (5)

tronman
tronman

Reputation: 10115

Angular appears to now support ng-min and ng-max out of the box without the necessity of writing your own directives. See this Fiddle that is using Angular 1.4.2.

Upvotes: 2

Mukund Kumar
Mukund Kumar

Reputation: 23191

i have face the same problem. got success by using this directive :

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

      }
    }
  }
]);

i got this solution from this website. for entire code you can go for this website

Upvotes: -1

Blaise
Blaise

Reputation: 13479

I also wrote a custom directive for it:

Working demo: 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'
    };

}]);

Example use:

<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>

Upvotes: 0

Guillaume86
Guillaume86

Reputation: 14400

I wrote directives to fill the gap, ng-min and 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']);

Upvotes: 22

Mark Rajcok
Mark Rajcok

Reputation: 364677

Apparently we can't use {{}}s (i.e., interpolation) for the min and max fields. I looked at the source code and I found the following:

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

$interpolate is not called, just parseFloat, so you'll need to specify a string that looks like a number for min and max.

Upvotes: 9

Related Questions