我可以在指令中要求使用 ng-controller 设置控制器吗?

Can I require a controller, in a directive, set with ng-controller?

我有(某种程度上)以下 html:

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

控制器的外观并不重要:

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

我的指令是这样的:

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

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

在文档中,他们总是在 require-属性 中指定另一个指令,但它说这意味着您需要控制器。所以我想尝试这个解决方案。但是我收到错误

"Controller 'MyController', required by directive 'mySubDirective', can't be found".

如果由 ng-controller 设置,是否无法从指令中要求控制器?

你只能做:

require: "^ngController"

所以,你不能比这更具体,即你不能按名称要求 "MainCtrl""MyController",但它会给你控制器实例:

.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>

不过,我认为这不是一个好方法,因为它使指令非常依赖于使用它的地方。您可以按照\评论中的建议直接传递控制器实例 - 它使它更加明确:

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

但它仍然是一个过于通用的对象,很容易被您的指令的用户滥用。

相反,公开定义良好的 API(通过属性)并将引用传递给控制器​​中定义的 functions/properties:

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

您可以在指令 link 函数中使用 element.controller() 来测试由 ngController 指定的最近的控制器。此方法的一个限制是它不会告诉您它是哪个控制器。您可能有几种方法可以做到这一点,但我选择命名控制器构造函数,并将其公开在范围内,因此您可以使用 instanceof

// 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);
      }
    };
  })

})();

您可以在 http://plnkr.co/edit/AVmr7Eb7dQD70Mpmhpjm?p=preview

看到这个

但是,如果您嵌套了 ngController,并且您想要测试不一定最接近的那一个,这将不起作用。为此,您可以定义一个递归函数来遍历 DOM 树:

  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);
      }
    };
  })

您可以在 http://plnkr.co/edit/xM5or4skle62Y9UPKfwG?p=preview

看到这个

作为参考,docs 指出 controller 函数可用于查找由 ngController 指定的控制器:

By default retrieves controller associated with the ngController directive