0

I am using the example for ngMessages from this post:

How to add custom validation to an AngularJS form?

It works OK as long as the blacklist is a static list of items.

Working validation with static blacklist

I would like to dynamically generate the blacklist but the directive seems to render before the blacklist is populated.

This is the directive:

.directive('blacklist', function () {
    return {
        require: 'ngModel',
        link: function (scope, elem, attr, ngModel) {
            var blacklist = attr.blacklist.split(',');
            ngModel.$parsers.unshift(function (value) {
                ngModel.$setValidity('blacklist', blacklist.indexOf(value) === -1);
                return value;
            });
        }
    };
});

This is the input where the directive is used:

<input type="text" id="DocumentName" name="DocumentName" class="form-control"
       ng-model="$ctrl.document.DocumentName" ng-required="true"
       blacklist="{{$ctrl.DocumentNames}}" />

In the controller when the blacklist is specified with static values it works as expected.

    .component('documentDetail', {
    templateUrl: '/app/document-detail/document-detail.template.html',
    controller: ['Document', 
        function DocumentDetailController(Document) {
            var self = this;
            self.DocumentNames = "Install Direct Bill Invoice,Order Preacknowledgement,Order Acknowledgement"; });

When this is changed to get the DocumentNames with a service call it seems like the directive is rendered before the blacklist values are populated.

component('documentDetail', {
    templateUrl: '/app/document-detail/document-detail.template.html',
    controller: ['Document',  
        function DocumentDetailController(Document) {
            var self = this;
            var documentProfiles = Document.query();
            documentProfiles.$promise.then(function () {
                var bl = [];
                for (var i = 0; i < documentProfiles.length; i++) {
                    bl.push(documentProfiles[i].DocumentName);
                }
                self.DocumentNames = bl.join(',');
            });

When I inspect the element I can see the data has been populated:

Inspect Element

But the validation acts like it is an empty string:

Validation Result

I tried wrapping it in a $timeout but the result was the same.

  component('documentDetail', {
    templateUrl: '/app/document-detail/document-detail.template.html',
    controller: ['Document', '$timeout', 
        function DocumentDetailController(Document, $timeout) {
            var self = this;
            var documentProfiles = Document.query();
            $timeout(function () {
                documentProfiles.$promise.then(function () {
                    var bl = [];
                    for (var i = 0; i < documentProfiles.length; i++) {
                        bl.push(documentProfiles[i].DocumentName);
                    }
                    self.DocumentNames = bl.join(',');
                });
            });

How can I get these values to populate before the directive or input renders so that the blacklist can be dynamic? Thanks in advance for your help.

georgeawg
  • 48,608
  • 13
  • 72
  • 95
markcee
  • 5
  • 3

1 Answers1

0

Use attr.$observe:

app.directive('blacklist', function () {
    return {
        require: 'ngModel',
        link: function (scope, elem, attrs, ngModel) {
            var blacklist = attrs.blacklist.split(',');
            attr.$observe("blacklist",function(newValue) {
                blacklist = newValue.split(',');
            });
            ngModel.$parsers.unshift(function (value) {
                ngModel.$setValidity('blacklist', blacklist.indexOf(value) === -1);
                return value;
            });
        }
    };
});

The observer function is invoked whenever the interpolated value changes.

For more information, see AngularJS attr.$observe API Reference


Update

Using the $validators API1

The accepted answer to the referenced question uses the $parsers and $formatters pipelines to add a custom synchronous validator. AngularJS 1.3+ added a $validators API so there is no need to put validators in the $parsers and $formatters pipelines:

app.directive('blacklist', function (){ 
   return {
      require: 'ngModel',
      link: function(scope, elem, attrs, ngModel) {           
          ngModel.$validators.blacklist = function(modelValue, viewValue) {
              var blacklist = attrs.blacklist.split(',');
              var value = modelValue || viewValue;
              var valid = blacklist.indexOf(value) === -1;
              return valid;
          });    
      }
   };
});

Notice that since the blacklist is re-computed everytime the ngModelController does a validation, there is no need to add an $observe function.

For more information, see AngularJS ngModelController API Reference - $validators.

Community
  • 1
  • 1
georgeawg
  • 48,608
  • 13
  • 72
  • 95