如何调用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()。操作按钮不在指令上。

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

338078 次浏览

假设动作按钮使用与指令相同的控制器$scope,只需在链接函数内部的$scope上定义函数updateMap。然后,当单击操作按钮时,控制器可以调用该函数。

<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()');
}
}
}
});

< a href = " http://jsfiddle.net/mrajcok/T96Zu/ " >小提琴< / >


根据@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;
};
}

< a href = " http://jsfiddle.net/mrajcok/T96Zu/1/ " >小提琴< / >

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

.
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>

基于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
};
}
}
};
});

您可以指定一个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>

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

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

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

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

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

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

  • $scope.$watch()应该替换事件的需求
  • 直接注入服务和调用方法对于直接通信也很有用
  • 指令之间可以通过指令控制器直接通信

有点晚了,但这是一个具有隔离作用域和在指令中调用函数的“事件”的解决方案。这个解决方案受到这篇SO文章 by satchmorun的启发,并添加了一个模块和一个API。

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


//Load dependency dynamically
angular.module('app').requires.push('MapModule');

创建一个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();
});
}
}
});

(也许你必须添加功能来删除事件。)

在指令中设置MapAPI的引用并添加$scope。MapApi. updateMap作为事件。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();
};
}

也许这不是最好的选择,但你可以通过angular.element("#element").isolateScope()$("#element").isolateScope()来访问指令的作用域和/或控制器。

使用范围。将被调用的函数关联到指令函数

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>

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

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

此方法不知道指令的$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的书中学习如何处理

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

使用美元的解析并在$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();
};
});

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

  1. 写一个自定义指令,从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);
    }
    }
    
  2. use it in the page controller's html:

    <my-directive controller="vm.myDirectiveController"></my-directive>
    
  3. Use the directive controller in the page controller:

    vm.myDirectiveController.callSomeMethod();
    

Note: the given solution works only for element directives' controllers (tag name is used to get the name of the wanted directive).

下面的解决方案将是有用的,当你有'控制器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

< >强测试

.

.

我的简单方法(将标记视为原始代码)

<html>
<div ng-click="myfuncion">
<my-dir callfunction="myfunction">
</html>


<directive "my-dir">
callfunction:"=callfunction"
link : function(scope,element,attr) {
scope.callfunction = function() {
/// your code
}
}
</directive>