Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular $compile with required controller

I have a composite list directive - that is - a list item that can be a list himself.
The parent directive defines the controller:

.directive('parent', function() {
    controller: function($scope) {
    },
    link: function (scope, element, attrs) {
    }
})

The list (of items) requires the parent controller which by itself works fine (why shouldn't it..):

.directive('list', function() {
     require: '^parent',
     link: function (scope, element, attrs, parentCtrl) {
     }
  })

The same goes as well for the concrete item, which is also fine:

.directive('item', function() {
    require: '^parent',
    link: function (scope, element, attrs, parentCtrl) {
    }
})

An item may be a composite in which case it creates a "list" himself. This composition is done by $compile (ing) a list item inside the link function:

link: function (scope, element, attrs, parentCtrl) {
      ...
      $compile("<list></list>")(scope)
      ... 
}

Which throws an exception:
Controller 'parent', required by directive 'list', can't be found!

The reason for this is obvious - the $compile function didn't provide the controller and therefore the requirement of 'parent' cannot be resolved.
And so I've tried providing the controller manually:

$compile("<list></list>")(scope, null, {'parent': parentCtrl});

Which doesn't throws an exception but still doesn't provide this controller when needed.

Any idea how to make the $compile function accept an external controllers which should be evaluated as well?

like image 769
Tomer Avatar asked Feb 18 '14 13:02

Tomer


2 Answers

I've just had a similar issue, and the solution seems to be to first add the element to the parent, and then compile it.

.directive('item', function($compile) {
  return {
    template:'<li><a ng-click="addSubList()">Create Another List</a></li>',
    require: '^parent',
    replace: true,
    link: function(scope, element, attrs, parentCtrl) {

      scope.addSubList = function() {
        var sublist = angular.element('<ul list>');
        element.find('a').append(sublist);
        $compile(sublist)(scope);
      };

    }
  };
});

See this Plunker: http://plnkr.co/edit/dASASrFbtXSMCRZKRAj5?p=preview

like image 68
lpiepiora Avatar answered Nov 01 '22 23:11

lpiepiora


For future reference, here is the solution:

On the $compile function the required controller can be passed as the transcluded controller:

$compile(template)(scope, undefined, {transcludeControllers: injectedCtrl})

Where the "injectedCtrl" is the object which lists controllers the directive expects, for example if you require: '^dad', then transcludeControllers look like this:

 transcludeControllers: {
        dad: { //name of controller in 'require' statement
          instance: vm //instance of controller
        }
      }

See this example: https://jsfiddle.net/qq4gqn6t/11/


Thats it!

like image 9
Tomer Avatar answered Nov 01 '22 21:11

Tomer