Angularjs 指令中的访问控制器作用域

Angularjs 指令中的访问控制器作用域,angularjs,isolate-scope,Angularjs,Isolate Scope,我创建了一个简单的指令,用于显示我正在创建的的排序列标题 ngGrid.directive("sortColumn", function() { return { restrict: "E", replace: true, transclude: true, scope: { sortby: "@", onsort: "=" }, templat

我创建了一个简单的指令,用于显示我正在创建的
的排序列标题

ngGrid.directive("sortColumn", function() {
    return {
        restrict: "E",
        replace: true,
        transclude: true,
        scope: {
            sortby: "@",
            onsort: "="
        },
        template: "<span><a href='#' ng-click='sort()' ng-transclude></a></span>",
        link: function(scope, element, attrs) {
            scope.sort = function () {

                // I want to call CONTROLLER.onSort here, but how do I access the controller scope?...
                scope.controllerOnSort(scope.sortby);
            };
        }
    };
});
ngGrid.directive(“sortColumn”,function()){ 返回{ 限制:“E”, 替换:正确, 是的, 范围:{ 肮脏的:“@”, onsort:“=” }, 模板:“”, 链接:函数(范围、元素、属性){ scope.sort=函数(){ //我想在这里调用CONTROLLER.onSort,但如何访问控制器作用域?。。。 scope.controllerOnSort(scope.sortby); }; } }; }); 下面是一些正在创建的表标题的示例:

<table id="mainGrid" ng-controller="GridCtrl>
<thead>
    <tr>
        <th><sort-column sortby="Name">Name</sort-column></th>
        <th><sort-column sortby="DateCreated">Date Created</sort-column></th>
        <th>Hi</th>
    </tr>
</thead>

指令中的
要求使用
ngController
并将链接功能修改为:

ngGrid.directive("sortColumn", function() {
    return {
        ...
        require: "ngController",
        ...
        link: function(scope, element, attrs, ngCtrl) {
            ...
        }
    };
});
你得到的是你的控制器,
GridCtrl
。但你不了解它的范围;您必须按照以下方式进行操作:

xxxx.controller("GridCtrl", function($scope, ...) {
    // add stuff to scope as usual
    $scope.xxxx = yyyy;

    // Define controller public API
    // NOTE: USING this NOT $scope
    this.controllerOnSort = function(...) { ... };
});
从link函数调用它,简单如下:

ngCtrl.controllerOnSort(...);
请注意,这需要获得第一个父级
ngController
。如果在
GridCtrl
和指令之间指定了另一个控制器,您将得到该控制器

演示原理的小提琴(使用方法访问父控制器的指令):


人们担心这种解决方案可能会引入不必要的紧耦合。如果这确实是一个问题,可以通过以下方式解决:

创建一个与控制器并排的指令,让我们称之为
master

<table id="mainGrid" ng-controller="GridCtrl" master="controllerOnSort()">

使用
$parse
服务,可以从主控制器的成员方法调用指定的方法。请参阅更新的fiddle实现此原则:

创建第二个指令作为包装器:

ngGrid.directive("columnwrapper", function() {
  return {
    restrict: "E",
    scope: {
      onsort: '='
    }
  };
});
然后,您只需在outer指令中引用要调用的函数一次:

<sort-column onSort="controllerOnSort" sortby="Name">Name</sort-column>
require: "^master"
<columnwrapper onsort="controllerOnSort">
  <sort-column sortby="Name">Name</sort-column>
  <sort-column sortby="DateCreated">Date Created</sort-column>
</columnwrapper>
请参阅此小提琴以了解工作示例:

当然,如果您不关心硬编码的依赖项,那么您也可以只使用一个指令,通过

我还有一把小提琴,上面写着:

此解决方案将与另一个答案()中的解决方案具有相同的效果(在硬耦合方面具有相同的批评),但至少比该解决方案简单一些。不管怎样,如果你努力结合,我不认为引用控制器有什么意义,因为它很可能一直在$scope.$parent中可用(但要注意设置作用域的其他元素)


不过,我会选择第一种解决方案。它添加了一些小标记,但解决了问题并保持了清晰的分离。另外,如果将第二个指令用作直接包装器,则可以确保$scope.$parent与外部指令匹配。

还有另一种方法可以做到这一点,尽管鉴于我相对缺乏经验,我无法说明这种解决方案是否适合。我无论如何都会把它传下去,仅供参考

在列中,创建范围变量属性:

<sort-column data-sortby="sortby">Date Created</sort-column>
然后在控制器中添加排序功能:

$scope.onSort = function(val) {
    $scope.sortby = val;
}
$scope.$on("_sortFinished", function(event, message){
   ..do something...  
});
然后在标记框中单击:

<sort-column data-sortby="sortby" ng-click="onSort('DateCreated')">Date Created</sort-column>
在“link:”函数中添加$watch:

scope.$watch('sortby', function () {
    ... your sort logic here ...
}
这种方法的美妙之处在于,指令完全解耦,您不需要从指令中调用onSort,因为在执行路径的这一部分期间,您实际上没有将onSort留在控制器中

如果需要告诉控制器等待排序完成,可以在控制器中定义事件:

$scope.onSort = function(val) {
    $scope.sortby = val;
}
$scope.$on("_sortFinished", function(event, message){
   ..do something...  
});
然后,在指令中,只需发出事件,即可完成该过程:

$scope.$emit('_sortFinished');
还有其他方法可以做到这一点,这种方式增加了一些紧密耦合,因为你的控制器必须监听。您的指令必须发出特定的甚至。。。但这对你来说可能不是问题,因为他们无论如何都是密切相关的

&local scope属性允许指令的使用者传入指令可以调用的函数

见详情


下面是一个示例,它显示了如何从指令代码在回调函数中传递参数。

称我为疯子,但似乎更容易通过内置方法从元素中获取控制器,而不是摆弄
require

var mod = angular.module('something', []).directive('myDir', 
  function () {
    return {
      link: function (scope, element) {
        console.log(element.controller('myDir'));
      },
      controller: function () {
        this.works = function () {};
      },
      scope: {}
    }
  }
);

我不确定以这种方式将指令和控制器紧密耦合是最好的主意。它肯定会起作用,但无论何时使用此指令,都需要在控制器上定义一个非常特定的方法。他目前的做法可以说是更好的实践。事实上,这些组件已经耦合在一起了,因为一个组件需要另一个组件才能正确运行。该指令取决于是否存在具有特定方法的控制器(任何控制器),此处为
controllerOnSort()
。这相当于让控制器在强类型langs中实现接口。父控制器可以通过
require:“^ngController”
规范设置为可选。不正确,因为如果您查看他的问题,他是通过属性传入回调。该名称将指令与任何控制器或作用域解耦。在他的示例中,排序方法可以命名为任何名称,而您的排序方法需要严格的命名。他的示例还允许从层次结构中的任何范围传入方法,而您的示例则查找直接父级。他的抱怨仅仅是他必须在所有专栏中重复这个属性。事实上,这是一个折衷的方法:你获得了便利,你失去了耦合(理论上,因为这些东西已经在逻辑上耦合了),感谢你的彻底回应。“紧密耦合”是一个有效的观点,尽管对于这种用法我并不介意,因为我只希望该指令与网格控件一起使用,所以我认为在这种情况下紧密耦合没有什么大问题
$scope.$emit('_sortFinished');
var mod = angular.module('something', []).directive('myDir', 
  function () {
    return {
      link: function (scope, element) {
        console.log(element.controller('myDir'));
      },
      controller: function () {
        this.works = function () {};
      },
      scope: {}
    }
  }
);