如何调用AngularJS指令中定义的方法?
我有一个指令,这里是代码:
.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()
。 操作button不在指令上。
什么是从控制器调用updateMap()
的最佳方式?
如果你想使用独立的作用域,你可以使用控制器作用域的一个variables的双向绑定=
来传递一个控制对象。 您也可以控制具有相同控制对象的页面上的相同指令的多个实例。
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>
假设操作button使用与指令相同的控制器$scope
,只需在链接函数的$scope
定义函数updateMap
。 当点击操作button时,您的控制器可以调用该function。
<div ng-controller="MyCtrl"> <map></map> <button ng-click="updateMap()">call updateMap()</button> </div>
app.directive('map', function() { return { restrict: 'E', replace: true, template: '<div></div>', link: function($scope, element, attrs) { $scope.updateMap = function() { alert('inside updateMap()'); } } } });
小提琴
根据@ FlorianF的评论,如果指令使用了一个独立的范围,事情就更复杂了。 这里有一个方法可以使它工作:在map
指令中添加一个set-fn
属性,它将向控制器注册指令函数:
<map set-fn="setDirectiveFn(theDirFn)"></map> <button ng-click="directiveFn()">call directive function</button>
scope: { setFn: '&' }, link: function(scope, element, attrs) { scope.updateMap = function() { alert('inside updateMap()'); } scope.setFn({theDirFn: scope.updateMap}); }
function MyCtrl($scope) { $scope.setDirectiveFn = function(directiveFn) { $scope.directiveFn = directiveFn; }; }
小提琴
尽pipe在一个指令的独立范围内暴露一个对象以促进与它的通信可能是诱人的,但是干扰可能会导致混淆的“意大利面条”代码,特别是如果你需要通过几个级别(控制器,指令,嵌套指令等)
我们原来走了这条路,但经过一些更多的研究发现,它更有意义,并导致更可维护和可读的代码来暴露事件和属性,指令将用于通过服务进行通信,然后使用$ watch在该服务的属性指令或任何其他需要对这些通信变化作出反应的控制。
这个抽象与AngularJS的dependency injection框架非常吻合,因为您可以将服务注入到任何需要对这些事件作出反应的项目中。 如果你看一下Angular.js文件,你会发现那里的指令也以这种方式使用了服务和$ watch,它们不会将事件暴露在隔离的范围之内。
最后,如果您需要在相互依赖的指令之间进行通信,我build议在这些指令之间共享一个控制器作为通信手段。
AngularJS的Wiki最佳实践也提到了这一点:
对于primefaces事件,只能使用。$ broadcast(),。$ emit()和。$ on()在整个应用程序中全局相关的事件(例如用户身份validation或应用程序closures)。 如果您想要特定于模块,服务或小部件的事件,您应该考虑服务,指令控制器或第三方库
- $ scope。$ watch()应该replace事件的需要
- 直接注入服务和调用方法也是有用的
- 指令能够通过指令控制器直接相互通信
在Oliver的答案的基础上 – 你可能不总是需要访问一个指令的内部方法,在这种情况下,你可能不希望创build一个空白对象,并添加一个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 }; } } }; });
有点迟了,但这是一个解决scheme,isololated范围和“事件”调用指令中的函数。 这个解决scheme受到了satchmorun的SO职位的启发 ,增加了一个模块和一个API。
//Create module var MapModule = angular.module('MapModule', []); //Load dependency dynamically angular.module('app').requires.push('MapModule');
创build一个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(); }); } } });
(也许你必须添加funcitonality来消除事件。)
在指令中设置对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); } } });
在“主”控制器中添加对MapApi的引用,并调用MapApi.updateMap()来更新地图。
app.controller('mainController', function ($scope, MapApi) { $scope.updateMapButtonClick = function() { MapApi.updateMap(); }; }
说实话,我没有真正相信任何在这个线程的答案。 所以,这是我的解决scheme:
指令处理程序(经理)方法
这个方法不知道指令的$scope
是共享的还是孤立的
一个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; } });
指令代码中,我通常把指令控制器内部没有处理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>
使用factory
访问控制器实例并运行公开的方法
angular.module('myModule').controller('MyController', function(MyDirectiveHandler, $scope) { $scope.someFn = function() { MyDirectiveHandler.get('foo').updateMap(); }; });
Angular的方法
从他们的angular度来看,他们是如何处理的
<form name="my_form"></form>
在$parent
作用域上使用$ parse和注册控制器。 这种技术不适用于孤立的$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>
只要使用范围。$ parent来关联调用指令函数的函数
angular.module('myApp', []) .controller('MyCtrl',['$scope',function($scope) { }]) .directive('mydirective',function(){ function link(scope, el, attr){ //use scope.$parent to associate the function called to directive function scope.$parent.myfunction = function directivefunction(parameter){ //do something } } return { link: link, restrict: 'E' }; });
在HTML中
<div ng-controller="MyCtrl"> <mydirective></mydirective> <button ng-click="myfunction(parameter)">call()</button> </div>
您可以将方法名称告知指令,以定义您想要从控制器调用的对象,但没有隔离范围,
angular.module("app", []) .directive("palyer", [ function() { return { restrict: "A", template:'<div class="player"><span ng-bind="text"></span></div>', link: function($scope, element, attr) { if (attr.toPlay) { $scope[attr.toPlay] = function(name) { $scope.text = name + " playing..."; } } } }; } ]) .controller("playerController", ["$scope", function($scope) { $scope.clickPlay = function() { $scope.play('AR Song'); }; } ]);
.player{ border:1px solid; padding: 10px; }
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script> <div ng-app="app"> <div ng-controller="playerController"> <p>Click play button to play <p> <p palyer="" to-play="play"></p> <button ng-click="clickPlay()">Play</button> </div> </div>
也许这不是最好的select,但是你可以使用angular.element("#element").isolateScope()
或$("#element").isolateScope()
来访问你的指令的作用域和/或控制器。
如何在页面控制器中获得指令的控制器:
-
编写一个自定义指令从DOM元素获取对指令控制器的引用:
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); } }
-
在页面控制器的html中使用它:
<my-directive controller="vm.myDirectiveController"></my-directive>
-
使用页面控制器中的指令控制器:
vm.myDirectiveController.callSomeMethod();
注意:给定的解决scheme仅适用于元素指令的控制器(标签名称用于获取所需指令的名称)。
下面的解决scheme将是有用的,当你有'控制器'格式的控制器(父母和指令(孤立))
有人可能会觉得这很有用,
指令:
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