我有一个指令,这是代码:

.directive('map', function() {
    return {
        restrict: 'E',
        replace: true,
        template: '<div></div>',
        link: function($scope, element, attrs) {

            var center = new google.maps.LatLng(50.1, 14.4); 
            $scope.map_options = {
                zoom: 14,
                center: center,
                mapTypeId: google.maps.MapTypeId.ROADMAP
            };
            // create map
            var map = new google.maps.Map(document.getElementById(attrs.id), $scope.map_options);
            var dirService= new google.maps.DirectionsService();
            var dirRenderer= new google.maps.DirectionsRenderer()

            var showDirections = function(dirResult, dirStatus) {
                if (dirStatus != google.maps.DirectionsStatus.OK) {
                    alert('Directions failed: ' + dirStatus);
                    return;
                  }
                  // Show directions
                dirRenderer.setMap(map);
                //$scope.dirRenderer.setPanel(Demo.dirContainer);
                dirRenderer.setDirections(dirResult);
            };

            // Watch
            var updateMap = function(){
                dirService.route($scope.dirRequest, showDirections); 
            };    
            $scope.$watch('dirRequest.origin', updateMap);

            google.maps.event.addListener(map, 'zoom_changed', function() {
                $scope.map_options.zoom = map.getZoom();
              });

            dirService.route($scope.dirRequest, showDirections);  
        }
    }
})

我想在用户操作上调用updateMap()。操作按钮不在指令上。

从控制器调用updateMap()的最佳方法是什么?


当前回答

老实说,我对这篇文章中的任何答案都不太信服。下面是我的解决方案:

指令处理程序(管理器)方法

该方法不知道指令的$作用域是共享作用域还是隔离作用域

一个用来注册指令实例的工厂

angular.module('myModule').factory('MyDirectiveHandler', function() {
    var instance_map = {};
    var service = {
        registerDirective: registerDirective,
        getDirective: getDirective,
        deregisterDirective: deregisterDirective
    };

    return service;

    function registerDirective(name, ctrl) {
        instance_map[name] = ctrl;
    }

    function getDirective(name) {
        return instance_map[name];
    }

    function deregisterDirective(name) {
        instance_map[name] = null;
    }
});

指令代码,我通常把所有不处理DOM的逻辑都放在指令控制器里。并在处理程序中注册控制器实例

angular.module('myModule').directive('myDirective', function(MyDirectiveHandler) {
    var directive = {
        link: link,
        controller: controller
    };

    return directive;

    function link() {
        //link fn code
    }

    function controller($scope, $attrs) {
        var name = $attrs.name;

        this.updateMap = function() {
            //some code
        };

        MyDirectiveHandler.registerDirective(name, this);

        $scope.$on('destroy', function() {
            MyDirectiveHandler.deregisterDirective(name);
        });
    }
})

模板代码

<div my-directive name="foo"></div>

使用工厂访问控制器实例并运行公开的方法

angular.module('myModule').controller('MyController', function(MyDirectiveHandler, $scope) {
    $scope.someFn = function() {
        MyDirectiveHandler.get('foo').updateMap();
    };
});

角的方法

从angular的书中学习如何处理

<form name="my_form"></form>

使用$parse并在$parent作用域上注册控制器。这种技术不适用于孤立的$scope指令。

angular.module('myModule').directive('myDirective', function($parse) {
    var directive = {
        link: link,
        controller: controller,
        scope: true
    };

    return directive;

    function link() {
        //link fn code
    }

    function controller($scope, $attrs) {
        $parse($attrs.name).assign($scope.$parent, this);

        this.updateMap = function() {
            //some code
        };
    }
})

在控制器中使用$scope.foo访问它

angular.module('myModule').controller('MyController', function($scope) {
    $scope.someFn = function() {
        $scope.foo.updateMap();
    };
});

其他回答

您可以指定一个DOM属性,该属性可用于允许指令在父作用域上定义函数。然后父作用域可以像调用其他方法一样调用此方法。这是一个活塞。下面是相关代码。

Clearfn是指令元素上的一个属性,父作用域可以将一个作用域属性传递给它,然后指令可以将该作用域属性设置为一个完成所需行为的函数。

<!DOCTYPE html>
<html ng-app="myapp">
  <head>
    <script data-require="angular.js@*" data-semver="1.3.0-beta.5" src="https://code.angularjs.org/1.3.0-beta.5/angular.js"></script>
    <link rel="stylesheet" href="style.css" />
    <style>
      my-box{
        display:block;
        border:solid 1px #aaa;
        min-width:50px;
        min-height:50px;
        padding:.5em;
        margin:1em;
        outline:0px;
        box-shadow:inset 0px 0px .4em #aaa;
      }
    </style>
  </head>
  <body ng-controller="mycontroller">
    <h1>Call method on directive</h1>
    <button ng-click="clear()">Clear</button>
    <my-box clearfn="clear" contentEditable=true></my-box>
    <script>
      var app = angular.module('myapp', []);
      app.controller('mycontroller', function($scope){
      });
      app.directive('myBox', function(){
        return {
          restrict: 'E',
          scope: {
            clearFn: '=clearfn'
          },
          template: '',
          link: function(scope, element, attrs){
            element.html('Hello World!');
            scope.clearFn = function(){
              element.html('');
            };
          }
        }
      });
    </script>
  </body>
</html>

下面的解决方案将是有用的,当你有'控制器As'格式的控制器(父控制器和指令(隔离))

有人可能会觉得这很有用,

指令:

var directive = {
        link: link,
        restrict: 'E',
        replace: true,
        scope: {
            clearFilters: '='
        },
        templateUrl: "/temp.html",
        bindToController: true, 
        controller: ProjectCustomAttributesController,
        controllerAs: 'vmd'
    };
    return directive;

    function link(scope, element, attrs) {
        scope.vmd.clearFilters = scope.vmd.SetFitlersToDefaultValue;
    }
}

控制器:

function DirectiveController($location, dbConnection, uiUtility) {
  vmd.SetFitlersToDefaultValue = SetFitlersToDefaultValue;

function SetFitlersToDefaultValue() {
           //your logic
        }
}

HTML代码:

      <Test-directive clear-filters="vm.ClearFilters"></Test-directive>
    <a class="pull-right" style="cursor: pointer" ng-click="vm.ClearFilters()"><u>Clear</u></a> 
//this button is from parent controller which will call directive controller function

如果你想使用隔离作用域,你可以使用控制器作用域变量的双向绑定=来传递一个控制对象。你也可以用相同的控制对象控制页面上相同指令的几个实例。

angular.module('directiveControlDemo', []) .controller('MainCtrl', function($scope) { $scope.focusinControl = {}; }) .directive('focusin', function factory() { return { restrict: 'E', replace: true, template: '<div>A:{{internalControl}}</div>', scope: { control: '=' }, link: function(scope, element, attrs) { scope.internalControl = scope.control || {}; scope.internalControl.takenTablets = 0; scope.internalControl.takeTablet = function() { scope.internalControl.takenTablets += 1; } } }; }); <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script> <div ng-app="directiveControlDemo"> <div ng-controller="MainCtrl"> <button ng-click="focusinControl.takeTablet()">Call directive function</button> <p> <b>In controller scope:</b> {{focusinControl}} </p> <p> <b>In directive scope:</b> <focusin control="focusinControl"></focusin> </p> <p> <b>Without control object:</b> <focusin></focusin> </p> </div> </div>

尽管将对象暴露在指令的孤立作用域上以方便与之通信可能很有吸引力,但这样做可能会导致令人困惑的“意大利面条”代码,特别是当你需要通过几个级别(控制器、指令、嵌套指令等)链接这种通信时。

我们最初是沿着这条路走的,但经过更多的研究后发现,它更有意义,并导致更可维护和可读的代码,以公开事件和属性,指令将通过服务进行通信,然后在指令中的服务属性上使用$watch或任何其他控件,这些控件需要对通信的这些变化做出反应。

这种抽象与AngularJS的依赖注入框架配合得非常好,因为你可以将服务注入到任何需要对这些事件做出反应的项中。如果你看一下Angular.js文件,你会发现里面的指令也以这种方式使用services和$watch,它们不会在孤立的作用域中公开事件。

最后,如果你需要在相互依赖的指令之间通信,我建议在这些指令之间共享一个控制器作为通信手段。

AngularJS的维基最佳实践也提到了这个:

Only use .$broadcast(), .$emit() and .$on() for atomic events Events that are relevant globally across the entire app (such as a user authenticating or the app closing). If you want events specific to modules, services or widgets you should consider Services, Directive Controllers, or 3rd Party Libs $scope.$watch() should replace the need for events Injecting services and calling methods directly is also useful for direct communication Directives are able to directly communicate with each other through directive-controllers

如何在页面控制器中获取指令的控制器:

write a custom directive to get the reference to the directive controller from the DOM element: angular.module('myApp') .directive('controller', controller); controller.$inject = ['$parse']; function controller($parse) { var directive = { restrict: 'A', link: linkFunction }; return directive; function linkFunction(scope, el, attrs) { var directiveName = attrs.$normalize(el.prop("tagName").toLowerCase()); var directiveController = el.controller(directiveName); var model = $parse(attrs.controller); model.assign(scope, directiveController); } } use it in the page controller's html: <my-directive controller="vm.myDirectiveController"></my-directive> Use the directive controller in the page controller: vm.myDirectiveController.callSomeMethod();

注意:给出的解决方案只适用于元素指令的控制器(标签名用于获取所需指令的名称)。