角度指令之间的依赖关系

时间:2015-01-27 12:58:14

标签: angularjs angularjs-directive

我创建了一个包含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

4 个答案:

答案 0 :(得分:4)

指令有一些合作/沟通方式

  1. 如果一方是另一方的兄弟或孩子,您可以使用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
            }
        };
    });
    
  2. 两个指令用于通信的服务。如果指令只能在每个视图中实例化一次,这很容易并且效果很好。

  3. $broadcast上使用$emit / $rootScope(稍有不同:$broadcast将“泛滥”范围层次结构,可能会影响效果; { {1}}只会调用$emit上的侦听器,但这意味着您必须执行$rootScope然后记住在当前范围被销毁时取消注册侦听器 - 意味着更多代码)。这种方法适用于解耦组件。在调试时可能会变得棘手,即找到该事件的来源(与所有基于事件的系统一样)。

  4. 其他

  5.   

    控制器,链接和编译

    很短:

    • 使用控制器定义指令的 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>