AngularJS: conditional ng-class with a function triggering ridiculous amounts

I checked similar Q&As but they didn't fix my issue.

I want to dynamically set the class of an input, consider the following Plunkr.

As you can see I have one field, defined like this:

<input type="text" ng-model="Person.Name" ng-class="{'mandatory': IsFieldMandatory('Name')}" />

That function has the following code:

self.IsFieldMandatory = function(field){
  var isMandatory = true;

  var value = $scope.Person.Name;

  $scope.Cnt = $scope.Cnt + 1;

  return value.length == 0;
}

As you can see, it triggers 11(!) times on a page load (or any further action like blur).

PS: The function does not work because I get this error:

Error: $rootScope:infdig Infinite $digest Loop

It occurs because of the $scope.Cnt increment. If put in comment, it works (but then I don't know the number of triggers).

PPS:

How can I use this?:

var value = $parse('Person.' + field);

That would be better because then the function would work for any field. Which it needs to, in the real world scenario. (this returns a function, not an actual value)

Thx!

UPDATE

The Plunkr example doesn't seem to be valuable, so instead the original function:

self.IsMandatory = function (field) {

    console.log('test');

    if (self.IsMandatoryFieldsLoaded == true) {
        var idxAsync = self.MandatoryFields.indexOf(field);
        return idxAsync > -1;
    }

    return false;
}

As you can see I don't do any modifications, just reads. I have 11 fields, yet this function's 'test' logging is written 198 times!

Answers:

Answer

You can simply check if there is anything typed in the input like so:

<input type="text" ng-model="Person.Name" ng-class="{'mandatory': !Person.Name.length}" />

This way you get rid of your error. Because you were updating the Cnt $scope variable in your function, the digest cycle was triggered over and over again. That caused the infinite loop (the error you were seeing).

By simply checking if Person.Name has a length, you avoid declaring a new function and other unnecessary logic. It's simple and much cleaner.

Hope this helped.

Update using ngForms:

You can declare a directive for validating your input like so:

app.directive('validate', function() {
  return {
    restrict: 'A',
    require: 'ngModel',
    link: function(scope, elem, attr, ngModelCtrl) {
      scope.$watch('Person.Name', function(newValue) {
        ngModelCtrl.$setValidity('notEmpty', isEmpty(newValue))
      });
    }
  }
});

function isEmpty(string) {
  return !!string.length;
}

and use it on your input

<input type="text" validate ng-model="Person.Name"/>

and in your CSS you will have

input.ng-invalid {
  border: 2px solid red;
}

Now you can delcare how many and complex validation functions and simply use the $setValiditity() function to tell Angular if that input is valid or not. If one of your validation will turn false, then Angular will automatically turn your input red by adding the ng-invalid class.

Tags

Recent Questions

Top Questions

Home Tags Terms of Service Privacy Policy DMCA Contact Us

©2020 All rights reserved.