Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Append child directive within parent directive

I have a parent directive where I want to dynamically add child directives in the link function. The child directives ^require the parentDirective. I'm able to add any html element but as soon as I try to $compile my child directive I get the following error that it can't find the required controller. If I manually add the child directives it works perfectly.

Error:

Error: [$compile:ctreq] Controller 'myInput', required by directive 'myKey', can't be found!

My template should look like this after adding the elements:

<myInput>
 <myKey></myKey> <-- added dynamically
 <myKey></myKey> <-- added dynamically
 <myKey></myKey> <-- added dynamically
  ....
</myInput>

myInput directive:

angular.module('myModule').directive('myInput', ['$log', '$templateCache', '$compile', function($log, $templateCache, $compile) {
  return {
    restrict: 'E',
    transclude: true,
    scope: {
      service: '=',            // expects a stimulus object provided by the tatoolStimulusService
      onkeydown: '&'           // method called on key press
    },
    controller: ['$scope', function($scope) {
      this.addKey = function(keyCode, value) {
        $scope.service.addInputKey(keyCode, { givenResponse: value });
      };
    }],
    link: function (scope, element, attr) {

      // add keys directives
      angular.forEach(scope.service.registeredKeyInputs, function(value, key) {
        var keyEl = angular.element(
          $compile('<myKey code="'+ key +'" response="'+ value.response +'"></myKey >')($rootScope));
        element.children(":first").append(keyEl);
      });

    },
    template: '<div ng-transclude></div>'
  };
}]);

myKey directive:

angular.module('myModule').directive('myKey', ['$log', '$sce', function($log, $sce) {
  return {
    restrict: 'E',
    scope: {},
    require: '^myInput',
    link: function (scope, element, attr, myCtrl) {
      myCtrl.addKey(attr.code, attr.response);

      // ...
    },
    template: '<div class="key"><span ng-bind-html="key"></span></div>'
  };
}]);
like image 348
jimmy Avatar asked Nov 06 '14 23:11

jimmy


1 Answers

Change the order of compile-append operations to append-compile:

var keyEl = angular.element('<myKey code="'+ key +'" response="'+ value.response +'"></myKey>');
element.append(keyEl);
$compile(keyEl)(scope);

Apparently it's important in this case (locating parent element directive), that new element being compiled was already in DOM.

Unless DOM element is appended to DOM it doesn't have a parent (its parentNode property is null). When Angular looks for ^myInput it traverses up the DOM tree until it finds a node with required directive. If the element is not in DOM yet, this search fails immediately, because element doesn't have a single parentNode. Hence the error you are getting.

Also I recommend to change names of your directives from camelCase to snake-case:

<my-input>
    <my-key></my-key>
</my-input>

Then compiling part will also change:

angular.element('<my-key code="'+ key +'" response="'+ value.response +'"></my-key >');
like image 107
dfsq Avatar answered Oct 16 '22 09:10

dfsq