我创建了一个简单的指令,显示我正在创建的<table>
的排序列标题。
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);
};
}
};
});
以下是一些正在创建的表标题的示例:
<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>
因此,当单击排序列时,我想在我的网格控制器上触发onControllerSort函数..但是我被卡住了!到目前为止,我能够做到这一点的唯一方法是为每个<sort-column>
添加“onSort”的属性并引用指令中的那些:
<sort-column onSort="controllerOnSort" sortby="Name">Name</sort-column>
但是这不是很好,因为我总是想调用controllerOnSort,所以为每个指令管道都有点难看。如何在指令中执行此操作而不需要在HTML中使用不必要的标记?指令和控制器都在同一个模块中定义,如果有帮助的话。
答案 0 :(得分:23)
创建第二个指令作为包装器:
ngGrid.directive("columnwrapper", function() {
return {
restrict: "E",
scope: {
onsort: '='
}
};
});
然后你可以引用函数在外部指令中调用一次:
<columnwrapper onsort="controllerOnSort">
<sort-column sortby="Name">Name</sort-column>
<sort-column sortby="DateCreated">Date Created</sort-column>
</columnwrapper>
在“sortColumn”指令中,您可以通过调用
来调用该引用的函数scope.$parent.onsort();
请参阅此小提琴,了解一个工作示例:http://jsfiddle.net/wZrjQ/1/
当然,如果你不关心硬编码的依赖关系,你也可以继续使用一个指令,只需通过
调用父作用域上的函数(那将是有问题的控制器)。scope.$parent.controllerOnSort():
我还有另一个小提示:http://jsfiddle.net/wZrjQ/2
这个解决方案与其他答案(https://stackoverflow.com/a/19385937/2572897)中的解决方案具有相同的效果(在硬耦合方面具有相同的批评),但至少比该解决方案更容易一些。无论如何,如果你很难结合,我认为引用控制器没有意义,因为它很可能在$ scope。$ parent一直可用(但要注意设置范围的其他元素)。
但我会选择第一个解决方案。它增加了一些小标记,但解决了问题并保持了清晰的分离。如果你使用第二个指令作为直接包装,你也可以确定$ scope。$ parent匹配外部指令。
答案 1 :(得分:20)
&amp; local scope属性允许指令的使用者传入指令可以调用的函数。
详见here。
这是一个answer to a similar question,它显示了如何从指令代码传递回调函数中的参数。
答案 2 :(得分:8)
在您的指令中需要ngController
并将链接功能修改为:
ngGrid.directive("sortColumn", function() {
return {
...
require: "ngController",
...
link: function(scope, element, attrs, ngCtrl) {
...
}
};
});
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(...) { ... };
});
将链接功能简单地称为:
ngCtrl.controllerOnSort(...);
请注意,此要求将获得第一个父ngController
。如果在GridCtrl
和指令之间指定了另一个控制器,您将获得该控制器。
演示原则的小提琴(使用方法访问父ng-controller
的指令):http://jsfiddle.net/NAfm5/1/
人们担心这种解决方案可能会引入不必要的紧耦合。如果这确实是一个问题,可以解决:
创建一个与控制器并排的指令,让我们称之为master
:
<table id="mainGrid" ng-controller="GridCtrl" master="controllerOnSort()">
该指令引用控制器的所需方法(因此:解耦)。
子指令(在您的情况下为sort-column
)需要master
指令:
require: "^master"
使用$parse
服务,可以从主控制器的成员方法调用指定的方法。请参阅实施此原则的更新小提琴:http://jsfiddle.net/NAfm5/3/
答案 3 :(得分:2)
还有另一种方法可以做到这一点,虽然鉴于我相对缺乏经验,我不能说这种解决方案的适用性。无论如何我都会将其传递出去以供参考。
在列中,您可以创建范围变量属性:
<sort-column data-sortby="sortby">Date Created</sort-column>
然后在您的控制器中定义范围变量:
$scope.sortby = 'DateCreated' // just a default sort here
然后在控制器中添加排序功能:
$scope.onSort = function(val) {
$scope.sortby = val;
}
然后在你的标记中点击ng-click:
<sort-column data-sortby="sortby" ng-click="onSort('DateCreated')">Date Created</sort-column>
然后在您的指令中将sortby属性添加到指令范围:
scope: {
sortby: '=' // not sure if you need
}
在“link:”功能中添加$ watch:
scope.$watch('sortby', function () {
... your sort logic here ...
}
这种方法的优点IMO是你的指令完全解耦,你不需要从指令回调onSort,因为在执行路径的那一部分你并没有真正在控制器中保留onSort。 / p>
如果您需要告诉控制器等待排序完成,您可以在控制器中定义一个事件:
$scope.$on("_sortFinished", function(event, message){
..do something...
});
然后在你的指令中简单地发出事件,然后完成该过程:
$scope.$emit('_sortFinished');
还有其他方法可以做到这一点,这种方式会增加一些紧密耦合,因为你的控制器必须要监听。并且你的指令必须发出一个特定的偶数......但这对你来说可能不是问题,因为它们无论如何密切相关。
答案 4 :(得分:1)
叫我疯了,但是通过内置方法从元素中获取控制器似乎更容易,而不是摆弄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: {}
}
}
);