Samir Shah
Samir Shah

Reputation: 709

angularjs : how to restrict input type number to allow only even number with min and max limit as well as steps to increase

I am working on one requirement where I want to allow only even numbers to text box or number box(input type number). with minimum and maximum limit like from 4 to 14 and it should only increase by step of 2 if we have number box.

I tried with HTML input type number with min max and step attributes it's working fine but we can edit the text box with any number so to restrict I tried using directive but it's not working out for me. I will be glad if anyone can help me out with this.

HTML :

<body ng-controller="ctrl">

new : <number-only-input step="2" min="4" max="14" input-value="wks.number" input-name="wks.name" >

</body>

Script :

var app = angular.module('app', []);
app.controller('ctrl', function($scope){
    $scope.name = 'Samir Shah';
    $scope.price = -10;
    $scope.wks =  {number: '', name: 'testing'};
});

app.directive('numberOnlyInput', function () {
    return {
        restrict: 'EA',
        template: '<input type="text" name="{{inputName}}" ng-model="inputValue" />',
        scope: {
            inputValue: '=',
            inputName: '=',
            min: '@',
            max: '@',
            step: '@'
        },
        link: function (scope) {
            scope.$watch('inputValue', function(newValue,oldValue) {
                var arr = String(newValue).split("");
                if (arr.length === 0) return;
                if (arr.length === 1 && (arr[0] == '-' || arr[0] === '.' )) return;
                if (arr.length === 2 && newValue === '-.') return;
                if (isNaN(newValue)) {
                    scope.inputValue = oldValue;
                    return;
                }
                if(!isNaN(newValue)){
                        if(newValue < parseInt(scope.min) || newValue > parseInt(scope.max)){
                            scope.inputValue = oldValue;
                            return;
                        }
                }
            });
        }
    };
});

Upvotes: 6

Views: 9781

Answers (3)

Avinash Jain
Avinash Jain

Reputation: 7606

Why are you doing too much of work of a simple thing. Max length will not work with <input type="number" the best way I know is to use oninput event to limit the maxlength. Please see the below code, Its a generic solution work with all the Javascript framework.

<input name="somename"
    oninput="javascript: if (this.value.length > this.maxLength) this.value = this.value.slice(0, this.maxLength);"
    type = "number"
    maxlength = "6"
 />

Upvotes: 0

Denis Thomas
Denis Thomas

Reputation: 1022

You could define a property with getter and setter to process the entered value. If the value does not match the requrements display messages but not accept new value.

Using this method you could apply any validation logic, the second field editValue is needed because otherwise you could not enter an invalid number. Therefore editValue alows to enter numbers with numerous digits which will be partially invalid during entering the value.

Property:

  // Property used to bind input containing validation
  Object.defineProperty($scope, "number", {
    get: function() {
      return $scope.editValue;
    },
    set: function(value) {
      value = parseInt(value);
      $scope.editValue = value;
      var isValid = true;
      // Min?
      if (value < parseInt($scope.min)) {
        $scope.toSmall = true;
        isValid = false;
      } else {
        $scope.toSmall = false;
      }
      // Max?
      if (value > parseInt($scope.max)) {
        $scope.toBig = true;
        isValid = false;
      } else {
        $scope.toBig = false;
      }
      // Step not valid
      if (value % parseInt($scope.step) > 0) {
        $scope.stepNotValid = true;
        isValid = false;
      } else {
        $scope.stepNotValid = false;
      }
      $scope.isValid = isValid;
      if (isValid) {
        $scope.value = value;
      }
    }
  });


Working example

Below you can find a complete working example directive containing the property described above including increase/decrease buttons:

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

app.directive('numberOnlyInput', function() {
  return {
    restrict: 'E',
    template: '<input type="text" ng-model="number" ng-class="{\'error\': !isValid}"/><button ng-click="increase()">+</button><button ng-click="decrease()">-</button> Value: {{value}} {{stepNotValid ? (" value must be in steps of " + step) : ""}} {{toSmall ? " value must be greater or equal to " + min : ""}} {{toBig ? " value must be smaler or equal to " + max : ""}}',
    scope: {
      value: '=value',
      min: '@',
      max: '@',
      step: '@'
    },
    link: function($scope) {
      // Increase value
      $scope.increase = function() {
        var newValue = parseInt($scope.value) + parseInt($scope.step);
        if (newValue <= $scope.max) {
          $scope.number = newValue;
          $scope.editValue = $scope.number;
        }
      };
      // Decrease value
      $scope.decrease = function() {
        var newValue = parseInt($scope.value) - parseInt($scope.step);
        if (newValue >= $scope.min) {
          $scope.number = newValue;
          $scope.editValue = $scope.number;
        }
      };
      // Property used to bind input containing validation
      Object.defineProperty($scope, "number", {
        get: function() {
          return $scope.editValue;
        },
        set: function(value) {
          value = parseInt(value);
          $scope.editValue = value;
          var isValid = true;
          // Min?
          if (value < parseInt($scope.min)) {
            $scope.toSmall = true;
            isValid = false;
          } else {
            $scope.toSmall = false;
          }
          // Max?
          if (value > parseInt($scope.max)) {
            $scope.toBig = true;
            isValid = false;
          } else {
            $scope.toBig = false;
          }
          // Step not valid
          if (value % parseInt($scope.step) > 0) {
            $scope.stepNotValid = true;
            isValid = false;
          } else {
            $scope.stepNotValid = false;
          }
          $scope.isValid = isValid;
          if (isValid) {
            $scope.value = value;
          }
        }
      });
      // Init actual Value of the input element
      $scope.number = parseInt($scope.value);
      $scope.editValue = parseInt($scope.value);
    }
  };
});
app.controller('controller', function($scope) {
  $scope.value = 10;
});
.error {
  color: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp" ng-controller="controller">
  Number:
  <number-only-input min="4" max="14" step="2" value="value"></number-only-input>
</div>

Upvotes: 0

tuckerjt07
tuckerjt07

Reputation: 922

<form name="testForm">
<div ng-controller="MyCtrl">
    <input type="text" name="testInput" ng-model="number" ng-min="2" ng-max="14" required="required" numbers-only="numbers-only" />
    <div ng-show="testForm.testInput.$error.nonnumeric" style="color: red;">
        Numeric input only.
    </div>
    <div ng-show="testForm.testInput.$error.belowminimum" style="color: red;">
        Number is too small.
    </div>
    <div ng-show="testForm.testInput.$error.abovemaximum" style="color: red;">
        Number is too big.
    </div>
    <div ng-show="testForm.testInput.$error.odd" style="color: red;">
        Numeric is odd.
    </div>
</div>
</form>

angular.module('myApp', []).directive('numbersOnly', function () {
return {
    require: 'ngModel',
    link: function (scope, element, attrs, modelCtrl) {
        element.bind('blur', function () {
            if (parseInt(element.val(), 10) < attrs.ngMin) {
                modelCtrl.$setValidity('belowminimum', false);
                scope.$apply(function () {
                    element.val('');
                });
            }
        });
        modelCtrl.$parsers.push(function (inputValue) {
            // this next if is necessary for when using ng-required on your input. 
            // In such cases, when a letter is typed first, this parser will be called
            // again, and the 2nd time, the value will be undefined
            if (inputValue == undefined) return ''
            var transformedInput = inputValue.replace(/[^0-9]/g, '');
            if (transformedInput != inputValue || (parseInt(transformedInput, 10) < parseInt(attrs.ngMin, 10) && transformedInput !== '1') || parseInt(transformedInput, 10) > parseInt(attrs.ngMax, 10) || (transformedInput % 2 !== 0 && transformedInput !== '1')) {
                if (transformedInput != inputValue) {
                    modelCtrl.$setValidity('nonnumeric', false);
                } else {
                    modelCtrl.$setValidity('nonnumeric', true);
                }
                if (parseInt(transformedInput, 10) < parseInt(attrs.ngMin, 10) && transformedInput !== '1') {
                    modelCtrl.$setValidity('belowminimum', false);
                } else {
                    modelCtrl.$setValidity('belowminimum', true);
                }
                if (parseInt(transformedInput, 10) > parseInt(attrs.ngMax, 10)) {
                    modelCtrl.$setValidity('abovemaximum', false);
                } else {
                    modelCtrl.$setValidity('abovemaximum', true);
                }
                if (transformedInput % 2 !== 0 && transformedInput !== '1') {
                    modelCtrl.$setValidity('odd', false);
                } else {
                    modelCtrl.$setValidity('odd', true);
                }
                transformedInput = '';
                modelCtrl.$setViewValue(transformedInput);
                modelCtrl.$render();
                return transformedInput;
            }
            modelCtrl.$setValidity('nonnumeric', true);
            modelCtrl.$setValidity('belowminimum', true);
            modelCtrl.$setValidity('abovemaximum', true);
            modelCtrl.$setValidity('odd', true);
            return transformedInput;
        });
    }
};
});

Active fiddle http://jsfiddle.net/tuckerjt07/1Ldmkmog/

Upvotes: 1

Related Questions