0

We have the following scenario:

there is an container which can have x input elements (x >= 0) Each input element use normal ng-binds

In the container there is an function which adds an $parsers and $validators to each included ngModelController.

the code is the following:

<form-element>
   <fs-input-a ng-required="true" ng-model="model.streetname" name="streetname"></fs-input-a>
   <fs-input-b ng-required="true" ng-maxlength="5" ng-pattern="/\d$/" ng-model="model.streetnr" name="streetnr"></fs-input-b>
</form-element>

In the link method of the "form-element" directive we call something like this: var controls = element[0].querySelectorAll('[ng-model]'), i, ngModel;

// Get all ngModel controllers
if (controls.length) {
for (i = 0; i < controls.length; i++) {
    ngModel = angular.element(controls[i]).controller('ngModel');
    ngModel.$parsers.push(function(value) {
      ...
    }.bind(ngModel));
    ngModel.$validators.removeHidden = function() {
        // on validation remove the hideError flag
        this.$hideError = undefined;
        return true;
    }.bind(ngModel);
    }
}

This worked fine under AngularJS 1.4.x

In AngularJS 1.5.x it works only if the template in the input directives it directly defined via template: '...' If we use templateUrl: '...' we have now the problem that ngModel will be undefined. In the controls[i] I get the right element and there is a ng-model attribute but it seems that AngularJS 1.5 haven't compiled the element.

Is there a better way to manipulate the ngModels of the child elements?

Anti-g
  • 121
  • 1
  • 4

1 Answers1

0

Compilation is lazy, so there won't be a way to do this reliably the way you've got it currently, a parent directive has no way to know that its children are compiled and ready. $onInit is called when parent/sibling controllers are resolved and, from the Angular 1.5.3 docs, $postLink which is (most likely?) the last lifecycle method called when compiling a directive:

$postLink() - Called after this controller's element and its children have been linked. Similar to the post-link function this hook can be used to set up DOM event handlers and do direct DOM manipulation. Note that child elements that contain templateUrl directives will not have been compiled and linked since they are waiting for their template to load asynchronously and their own compilation and linking has been suspended until that occurs.

This seems to say that you shouldn't be trying to access children controllers through the DOM no matter what.

Is there any particular reason you need to loop through all of the controllers at the same time? If you're just doing the same initialisation to all of the controllers then an alternative would be to add the containers as a required controller in the children and do it there, e.g. in the child components:

require: {
  ...
  parent: '^parentComponent'
},
controller: function() {
  this.$onInit = () => this.parent.initialiseNgModel(...);
}

Alternatively, use ng-init or an attribute directive if the children can't be modified (or just to keep the code separate).

If it's absolutely necessary that the parent controller has to do all of them at once. Then a not very nice solution would be to add a way for the children components to signal that they've compiled and the parent counts how many there are and waits for all of them to be finished before continuing with your code.

Scott
  • 860
  • 8
  • 10