我创建了一个包含2个指令的小应用程序:一个添加谷歌地图div并初始化它,第二个显示包含标记的图层。指令是独立的,但我希望它们进行通信: layers 指令是独立的,但它需要使用google maps指令将标记添加到地图中。我通过$ rootScope使用$ broadcast在指令之间进行通信。
指令定义如下:
angular.module('googleMapModule', [])
.directive('myGoogleMap', function(){
template: '<div id="map" />',
controller: function($scope){
// some initializations
// Listen for event fired when markers are added
$scope.$on('addMarkersEvent', function(e, data){
// do something
}
}
}
angular.module('layersDirective', [])
.directive('myLayers', function() {
templateUrl: 'someLayersHtml.html',
controller: function($http, $scope, $rootScope){
// Get layers of markers, etc.
// On specific layer click, get markers and:
$rootScope.broadcast('addMarkersEvent', {
data: myMarkers
});
}
});
在这篇长篇序言之后,我的问题是:
如何实现两个指令之间的连接?使用$ rootScope和$ broadcast是否正确,或者myLayers指令和myGoogleMap指令之间是否存在依赖关系?
此外,我已经阅读过何时使用控制器,链接和编译,但我不知道在这里使用它们的正确方法。我的猜测是myGoogleMap应该在控制器中定义其API,myLayers应该依赖于myGoogleMap。
我在这里写的例子在我的应用程序中运行良好。我正在寻找有关如何正确行事的指导,并了解我在这里做错了什么。
谢谢,Rotem
答案 0 :(得分:4)
指令有一些合作/沟通方式
如果一方是另一方的兄弟或孩子,您可以使用require
。例如。对于这个模板:
<dir1>
<dir2></dir2>
</dir1>
使用此代码:
app.directive('dir1', function() {
return {
...
controller: function() {
// define api here
}
};
});
app.directive('dir2', function() {
return {
...
require: '^dir1',
link: function(scope, elem, attrs, dir1Controller) {
// use dir1 api here
}
};
});
两个指令用于通信的服务。如果指令只能在每个视图中实例化一次,这很容易并且效果很好。
在$broadcast
上使用$emit
/ $rootScope
(稍有不同:$broadcast
将“泛滥”范围层次结构,可能会影响效果; { {1}}只会调用$emit
上的侦听器,但这意味着您必须执行$rootScope
然后记住在当前范围被销毁时取消注册侦听器 - 意味着更多代码)。这种方法适用于解耦组件。在调试时可能会变得棘手,即找到该事件的来源(与所有基于事件的系统一样)。
其他
控制器,链接和编译
很短:
使用控制器定义指令的 API ,最好定义其大部分逻辑。请记住,元素,属性和任何转换函数分别作为$rootScope.$on()
,$element
和$attrs
可用于控制器。因此,在大多数情况下,控制器可以替换$transclude
函数。还要记住,与link
函数不同,控制器可以依赖注入。 (但是你仍然可以在指令级别执行依赖注入,因此,毕竟link
函数也可以访问依赖项。)
使用链接功能访问所需的控制器(参见上面的案例1)。或者,如果你感到懒惰,要定义指令的逻辑。我认为控制器更清洁。
使用编译功能... 很少 :)当您需要对模板进行非常特殊的转换时(重复是首先想到的 - 看link
)或其他神秘的东西。我一直使用指令,大约1%的指令需要编译功能。
我的猜测是myGoogleMap应该在控制器中定义其API,myLayers应该依赖于myGoogleMap
问题是您将如何使用事件传达此API?您可能只需要在自定义事件对象中创建API。您的活动的听众将使用该自定义活动。如果是这样,控制器实际上不需要定义API。
作为一个底线,我完全可以在您的案例中使用事件。
答案 1 :(得分:2)
通常应该处理指令之间的通信via controllers and using the require
property on the directive definition object。
如果我们重新处理你的第一个指令,我们可以简单地将该方法添加到控制器:
directive('myGoogleMap', function () {
return {
template: '<div id="map" />',
controller: function ($scope) {
var _this = this;
//Part of this directives API
_this.addMarkers = function(arg){
//Do stuff
}
}
};
});
现在我们可以在另一个指令中要求这个控制器,但一个鲜为人知的功能是你实际上可以要求一组指令。其中一条指令甚至可以是你自己。
所有这些内容将按顺序传递给link
函数的数组:
directive('myLayers', function () {
return {
templateUrl: 'someLayersHtml.html',
controller: function ($http, $scope, $rootScore) {
// Some get layers of markers functionality
},
// Require ourselves
// The '?' makes it optional
require: ['myLayers', '?myGoogleMap'],
link: function(scope, elem, attrs, ctrls){
var myLayersCtrl = ctrls[0];
var myGoogleMapCtrl = ctrls[1];
//something happens
if(myGoogleMapCtrl) {
myGoogleMapCtrl.addMarkers(markers);
}
}
};
});
现在,您可以使用?
明确地与选择加入进行沟通,这使得控制器可选。
答案 2 :(得分:1)
为了使其起作用,您必须在同一模块中定义两个指令,即:
var module = angular.module('myModule');
module.directive('myGoogleMap', function(){
template: '<div id="map" />',
controller: function($scope){
// some initializations
// Listen to event for adding markers
$scope.$on('addMarkersEvent', function(e, data){
// do something
}
}
}
module.directive('myLayers', function() {
templateUrl: 'someLayersHtml.html',
controller: function($http, $scope, $rootScore){
// Some get layers of markers functionality
// On specific layer click, get markers and:
$rootScope.broadcast('addMarkersEvent', {
data: myMarkers
});
}
});
了解更多here。
修改强> 抱歉,我不明白您的问题,但根据您的评论,请引用AngularJs Best Practices:
- 仅对原子事件使用。$ broadcast(),. $ emit()和。$ on() 在整个应用程序(例如用户)中全局相关 验证或应用程序关闭)。如果你想要特定的事件 您应该考虑的服务,指令模块,服务或小部件 控制器或第三方库
$scope.$watch()
应取代对事件的需求- 直接注入服务和调用方法也是如此 对于直接沟通很有用
- 指令能够通过指令控制器直接相互通信
答案 3 :(得分:1)
您已经强调了使用rootscope进行通信的指令。
如果指令在同一个html层次结构上定义,则指令可以通信的另一种方式是使用directive controller function
。您在问题中也强调了这一点。它的方式是(假设在父html上定义myGoogleMap
),两个指令定义变为:
angular.module('googleMapModule', [])
.directive('myGoogleMap', function () {
template: '<div id="map" />',
controller: function ($scope) {
this.addMarkersEvent = function (data) {}
// some initializations
}
angular.module('layersDirective', [])
.directive('myLayers', function ($http, $rootScope) {
templateUrl: 'someLayersHtml.html',
require: '^myGoogleMap',
link: function (scope, element, attrs, myGoogleMapController) {
$scope.doWork = function () {
myGoogleMapController.addMarkersEvent(data);
}
}
});
这里使用child指令的require
属性。此外,不使用子控制器,而是将子指令中的所有功能添加到link
函数中。这是因为子link
函数可以访问父指令控制器。
请注意,将两个指令添加到单个模块中(更新:实际上,只要主应用程序模块中有引用,您就可以将指令放在不同的模块中。)< / p>