3

I have a directive that prevents certain values from being entered into an input. For example this...

<input type="number" name="testValue" ng-model="testValue" 
    block="[0, 10, 17]" />

... will give a validation error if the user enters 0, 10, or 17.

This is working fine, but now I need to conditionally set the values being blocked depending on a variable in my controller, so I tried to use a ternary to set the value, like this...

<input type="number" name="testValue" ng-model="testValue" 
    block="blockValues ? [0, 10, 17] : []" />

However this is causing an Error: $rootScope:infdig Infinite $digest Loop, and I don't understand why. Could someone please explain this error to me and what I can do to fix it? Or any alternative would be appreciated as well. Thank you!

Here is a code snippet to demonstrate:

var myApp = angular.module("myApp", []); 
myApp.controller("myCtrl", function($scope) {
    $scope.blockValues = true;
    $scope.testValue = '';
});


myApp.directive('block', function() {
        'use strict';
        return {
            restrict: 'A',
            require: 'ngModel',
            scope: {
                block: '='
            },
            link: function(scope, element, attrs, ngModel) {
                ngModel.$validators.allowedValues = function (value) {
                    return !scope.block || scope.block.indexOf(value) === -1;
                };
            }
        };
    });
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.7/angular.min.js"></script>

<div ng-app="myApp" ng-controller="myCtrl">
    <form name="myForm">
      <!-- Errors -->
      <input type="number" name="testValue" ng-model="testValue"
        block="blockValues ? [0] : []" />

      <!-- No Errors
      <input type="number" name="testValue" ng-model="testValue"
        block="false ? [0] : []" />
      -->
      <!-- No Errors
      <input type="number" name="testValue" ng-model="testValue"
        block="true ? [0] : []" />
      -->
      <!-- No Errors
      <input type="number" name="testValue" ng-model="testValue"
        block="[0]" />
       -->
       <!-- No Errors
      <input type="number" name="testValue" ng-model="testValue"
        ng-min="blockValues ? 1 : 0" />
       -->      
    </form>
    <div>{{myForm.testValue.$error}}</div>
</div>

0

2 Answers 2

2

One common mistake is binding to a function which generates a new array every time it is called. In this case a new array [0] is generated each time, creating an infinite digest error.

  <!-- Infinite Digest Errors
  <input type="number" name="testValue" ng-model="testValue"
    block="blockValues ? [0] : []" />
  -->

Since it returns a new array, AngularJS determines that the model is different on each $digest cycle, resulting in the error. The solution is to return the same array object if the elements have not changed:

  <!-- FIXED -->
  <input type="number" name="testValue" ng-model="testValue"
    ng-init="a1=[0];a0=[]" block="blockValues ? a1 : a0" />

For more information, see AngularJS Error Reference - $rootScope / infdig

The Demo

var myApp = angular.module("myApp", []); 
myApp.controller("myCtrl", function($scope) {
    $scope.blockValues = true;
    $scope.testValue = '';
});


myApp.directive('block', function() {
        'use strict';
        return {
            restrict: 'A',
            require: 'ngModel',
            scope: {
                block: '='
            },
            link: function(scope, element, attrs, ngModel) {
                ngModel.$validators.allowedValues = function (value) {
                    return !scope.block || scope.block.indexOf(value) === -1;
                };
            }
        };
    });
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.7/angular.min.js"></script>

<div ng-app="myApp" ng-controller="myCtrl">
    <form name="myForm">
      <!-- Infinite Digest Errors
      <input type="number" name="testValue" ng-model="testValue"
        block="blockValues ? [0] : []" />
      -->

      <!-- FIXED -->
      <input type="number" name="testValue" ng-model="testValue"
        ng-init="a1=[0];a0=[]" block="blockValues ? a1 : a0" />
      
    </form>
    <div>{{myForm.testValue.$error}}</div>
</div>

Sign up to request clarification or add additional context in comments.

2 Comments

I read that angular link 3 times and didn't put it together, thank you. One follow up question, why did block="true ? [0] : []" or ng-min="blockValues ? 1 : 0" not cause errors?
1 and 0 are primitives. [0] is an object, a newly created object. [0] is not equal to [0] because they are different objects. See for yourself, console.log([0] == [0]) prints false.
0

I found using ng-model-options helped:

ng-model-options="{ allowInvalid: true, debounce: 200 }"

Read up on the docs more, here: https://docs.angularjs.org/api/ng/directive/ngModelOptions

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.