Can I require a controller in a directive installed with ng controller?

I have (sort of) the following html:

    <div ng-controller="MyController">
       <my-sub-directive></my-sub-directive>
    </div>

      

what the controller looks like, it doesn't matter:

app.controller("MyController", function($scope) {
   $scope.foo = "bar";
})

      

and my directive looks like this:

function mySubDirective() {
        return {
            restrict: "E",
            templateUrl:"aTemplate.html",
            require: "^MyController",
            link: function($scope, element) {

            }
        };
    }
app.directive("mySubDirective", mySubDirective);

      

In the documentation, they always specify a different directive in require

-property, but it says that this means you need a controller. So I wanted to try this solution. However, I am getting the error

"The controller" MyController "required by the directive" mySubDirective "could not be found."

Is it not possible to request a controller from a directive if set by ng controller?

+3


source to share


2 answers


You can only do:

require: "^ngController"

      

So, you cannot be more specific than that, i.e. you cannot query "MainCtrl"

or "MyController"

by name, but it will get an instance of the controller:

.controller("SomeController", function(){
   this.doSomething = function(){
     //
   };
})

.directive("foo", function(){
  return {
    require: "?^ngController",
    link: function(scope, element, attrs, ctrl){
      if (ctrl && ctrl.doSomething){
        ctrl.doSomething();
      }
    }
  }
});

      

<div ng-controller="SomeController">
   <foo></foo>
</div>

      



I don't think this is a good approach, as it makes the directive very dependent on where it is used. You can follow the guidelines in the comments to pass the controller instance directly - this makes it somewhat more explicit:

<div ng-controller="SomeController as ctrl">
  <foo ctrl="ctrl"></foo>
</div>

      

but it is still too general and can be easily used by users of your directive.

Instead, output a well-defined API (via attributes) and pass references to functions / properties defined in the controller:

<div ng-controller="SomeController as ctrl">
  <foo do="ctrl.doSomething()"></foo>
</div>

      

+5


source


You can use element.controller()

in the function links to check for the closest controller specified ngController

. The limitation of this method is that it doesn't tell you which controller it is. There are probably several ways to do this, but I prefer to call the controller constructor and expose it in scope, so you can useinstanceof

// Deliberately not adding to global scope
(function() {

  var app = angular.module('my-app', []);

  // Exposed in so can do "instanceof" in directive
  function MyController($scope) {}
  app.controller('MyController', MyController);

  app.directive("foo", function(){
    return {
      link: function($scope, $element){
        var controller = $element.controller();

        // True or false depending on whether the closest 
        // ngController is a MyController
        console.log(controller instanceof MyController);
      }
    };
  })

})();

      

You can see this at http://plnkr.co/edit/AVmr7Eb7dQD70Mpmhpjm?p=preview

However, this won't work if you have nested ngController

s and want to test the one that isn't necessarily the closest. To do this, you can define a recursive function to navigate to the DOM tree:



  app.directive("foo", function(){

    function getAncestorController(element, controllerConstructor) {
      var controller = element.controller();
      if (controller instanceof controllerConstructor) {
        return controller;
      } else if (element.parent().length) {
        return getAncestorController(element.parent(), controllerConstructor);
      } else {
        return void(0); // undefined
      }
    }

    return {
      link: function(scope, element){
        var controller = getAncestorController(element, MyController);

        // The ancestor controller instance, or undefined
        console.log(controller);
      }
    };
  })

      

You can see this at http://plnkr.co/edit/xM5or4skle62Y9UPKfwG?p=preview

For the docs reference, point out that the function controller

can be used to find controllers specified in ngController

:

By default gets the controller associated with the ngController directive

0


source







All Articles