如何调用AngularJS指令中定义的方法?

mcb*_*jam 294 angularjs angularjs-directive

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

.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);  
        }
    }
})
Run Code Online (Sandbox Code Playgroud)

我想呼吁updateMap()用户采取行动.操作按钮不在指令上.

updateMap()从控制器呼叫的最佳方式是什么?

Oli*_*and 364

如果要使用隔离范围,可以使用=控制器范围中变量的双向绑定来传递控制对象.您还可以在具有相同控件对象的页面上控制同一指令的多个实例.

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;
      }
    }
  };
});
Run Code Online (Sandbox Code Playgroud)
<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>
Run Code Online (Sandbox Code Playgroud)

  • 我很好地解决了同样的问题.它工作,但它看起来像一个黑客...我希望角有一个更好的解决方案. (44认同)
  • +1这也是我在Angular中为可重用组件创建API的方法. (11认同)
  • 如果我没有弄错的话,这比被接受的答案更清晰,辛普森一家提到+1 (5认同)
  • 您应该检查以确保`scope.control`存在,否则其他使用该指令但不需要访问该指令的方法并且没有`control` attr的地方将开始抛出关于不存在的错误能够在`undefined`上设置属性 (4认同)

Mar*_*cok 71

假设动作按钮使用相同的控制器$scope的指令,只是定义功能updateMap$scope的链接功能里面.然后,您的控制器可以在单击操作按钮时调用该功能.

<div ng-controller="MyCtrl">
    <map></map>
    <button ng-click="updateMap()">call updateMap()</button>
</div>
Run Code Online (Sandbox Code Playgroud)
app.directive('map', function() {
    return {
        restrict: 'E',
        replace: true,
        template: '<div></div>',
        link: function($scope, element, attrs) {
            $scope.updateMap = function() {
                alert('inside updateMap()');
            }
        }
    }
});
Run Code Online (Sandbox Code Playgroud)

fiddle


根据@ FlorianF的评论,如果指令使用隔离范围,事情会更复杂.这是使其工作的一种方法:set-fnmap指令添加一个属性,该指令将向控制器注册指令函数:

<map set-fn="setDirectiveFn(theDirFn)"></map>
<button ng-click="directiveFn()">call directive function</button>
Run Code Online (Sandbox Code Playgroud)
scope: { setFn: '&' },
link: function(scope, element, attrs) {
    scope.updateMap = function() {
       alert('inside updateMap()');
    }
    scope.setFn({theDirFn: scope.updateMap});
}
Run Code Online (Sandbox Code Playgroud)
function MyCtrl($scope) {
    $scope.setDirectiveFn = function(directiveFn) {
        $scope.directiveFn = directiveFn;
    };
}
Run Code Online (Sandbox Code Playgroud)

fiddle


Alw*_*ing 34

虽然在指令的隔离范围上公开一个对象以便于与它进行通信可能很诱人,但这样做可能会导致混乱的"意大利面条"代码,特别是如果你需要通过几个层次链接这种通信(控制器,指令,嵌套指令等)

我们最初沿着这条路走下去但是经过一些研究后发现它更有意义并且导致更易于维护和可读的代码暴露事件和属性,指令将通过服务用于通信,然后在该服务的属性中使用$ watch指令或任何其他需要对这些通信变更作出反应的控制.

这种抽象与AngularJS的依赖注入框架非常吻合,因为您可以将服务注入需要对这些事件做出反应的任何项目.如果查看Angular.js文件,您会看到其中的指令也以这种方式使用services和$ watch,它们不会在隔离范围上公开事件.

最后,如果您需要在彼此依赖的指令之间进行通信,我建议在这些指令之间共享一个控制器作为通信方式.

AngularJS的最佳实践Wiki也提到了这一点:

仅对原子事件使用.$ broadcast(),. $ emit()和.$ on()在整个应用程序中全局相关的事件(例如用户身份验证或应用程序关闭).如果您需要特定于模块,服务或小部件的事件,则应考虑服务,指令控制器或第三方库

  • $ scope.$ watch()应该取代事件的需要
  • 直接注入服务和调用方法对于直接通信也很有用
  • 指令能够通过指令控制器直接相互通信

  • 我直观地得出了两个解决方案:(1)观察范围变量`=`的变化,变量包含方法名和参数.(2)将单向绑定字符串`@`作为主题id公开,让被调用者在该主题上发送事件.现在我看到了最佳实践维基.我认为有理由不以这种方式做到这一点.但我还不是很清楚,它是如何运作的.在我的例子中,我创建了一个tabset指令,我想公开一个`switchTab(tabIndex)`方法.你能举个例子吗? (2认同)

Che*_*aks 15

基于Oliver的回答 - 您可能并不总是需要访问指令的内部方法,在这些情况下,您可能不希望创建空白对象并向control指令添加attr以防止它抛出错误(cannot set property 'takeTablet' of undefined).

您还可能希望在指令中的其他位置使用该方法.

我会添加一个检查以确保scope.control存在,并以与显示模块模式类似的方式设置方法

app.directive('focusin', function factory() {
  return {
    restrict: 'E',
    replace: true,
    template: '<div>A:{{control}}</div>',
    scope: {
      control: '='
    },
    link : function (scope, element, attrs) {
      var takenTablets = 0;
      var takeTablet = function() {
        takenTablets += 1;  
      }

      if (scope.control) {
        scope.control = {
          takeTablet: takeTablet
        };
      }
    }
  };
});
Run Code Online (Sandbox Code Playgroud)


Mud*_*Ali 12

说实话,我对这个帖子中的任何答案都不太相信.那么,这是我的解决方案:

指令处理程序(经理)方法

该方法与指令$scope是共享指令还是孤立指令无关

A factory注册指令实例

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;
    }
});
Run Code Online (Sandbox Code Playgroud)

指令代码,我通常把所有不处理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);
        });
    }
})
Run Code Online (Sandbox Code Playgroud)

模板代码

<div my-directive name="foo"></div>
Run Code Online (Sandbox Code Playgroud)

使用factory&运行公开的方法访问控制器实例

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

Angular的方法

从棱角分明的书中拿出一片关于它们如何处理的叶子

<form name="my_form"></form>
Run Code Online (Sandbox Code Playgroud)

使用$ 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
        };
    }
})
Run Code Online (Sandbox Code Playgroud)

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

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


Axd*_*der 10

有点晚了,但这是一个带有隔离范围和"事件"的解决方案,用于调用指令中的函数.此解决方案的灵感来自satchmorun的SO帖子,并添加了一个模块和一个API.

//Create module
var MapModule = angular.module('MapModule', []);

//Load dependency dynamically
angular.module('app').requires.push('MapModule');
Run Code Online (Sandbox Code Playgroud)

创建API以与指令通信.addUpdateEvent将事件添加到事件数组,updateMap调用每个事件函数.

MapModule.factory('MapApi', function () {
    return {
        events: [],

        addUpdateEvent: function (func) {
            this.events.push(func);
        },

        updateMap: function () {
            this.events.forEach(function (func) {
                func.call();
            });
        }
    }
});
Run Code Online (Sandbox Code Playgroud)

(也许你必须添加删除事件的功能.)

在指令中设置对MapAPI的引用,并在调用MapApi.updateMap时将$ scope.updateMap添加为事件.

app.directive('map', function () {
    return {
        restrict: 'E', 
        scope: {}, 
        templateUrl: '....',
        controller: function ($scope, $http, $attrs, MapApi) {

            $scope.api = MapApi;

            $scope.updateMap = function () {
                //Update the map 
            };

            //Add event
            $scope.api.addUpdateEvent($scope.updateMap);

        }
    }
});
Run Code Online (Sandbox Code Playgroud)

在"main"控制器中添加对MapApi的引用,只需调用MapApi.updateMap()来更新映射.

app.controller('mainController', function ($scope, MapApi) {

    $scope.updateMapButtonClick = function() {
        MapApi.updateMap();    
    };
}
Run Code Online (Sandbox Code Playgroud)

  • 如果您有多个相同类型的指令(取决于您的API服务),则此提议在现实世界中需要更多工作.您肯定会遇到需要仅从一个特定指令而不是所有指令调用函数的情况.您想通过解决方案来增强您的答案吗? (2认同)

Tre*_*vor 5

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

clearfn 是指令元素的一个属性,父作用域可以将一个scope属性传递到该属性,然后该指令可以设置为一个完成所需行为的函数.

<!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>
Run Code Online (Sandbox Code Playgroud)