html
<input my-datepicker id="1" /> <input my-datepicker id="2" />
javascript
module.directive('myDatepicker', function(){ return { link : function(scope, el){ el.datepicker(function(){ //这里需要执行在controller里定义的不同的回调函数 }); } } }); module.controller('testCtrl', function(){ vm.callbackA = function(){ //回调函数A }; vm.callbackB = function(){ //回调函数B } });
PHPz2017-04-10 15:00:46
之前在手机上草草回答了一下,现在补全。
一个指令使用多次,它们如何去绑定来自于父级(及以上)的表达式——比如方法调用?
@小俞 童鞋给出了一种答案,原理是多个指令共用父级作用域。这个方法能达到题主的要求么?能,但却是错的!
让指令继承父级作用域是编写指令的大忌之一,Angular 在设计之初给予指令太大的自由(同时太复杂的定义)一直都是被诟病的一个理由。事实上指令是 Angular 自己的叫法,它基本等价于 WebComponent 里的组件。你去创造一个组件,根本上图个什么?
@小俞 童鞋我问你,按你那样写指令的话,以上两点能满足哪一个?指令那样写还不如直接耦合进控制器里,等于没写呀。
好了,以上是针对评论的回复;以下是正解:
首先既然是指令,那么就要尽一切可能写成独立作用域的,最不济也得是新建作用域。到了现在这个时间点,不声明 scope
属性的指令等于没写指令(不信等着看 Angular 2.0,去年的 ngEurope 已经证实此点)。
接下来的问题就是独立作用域之后如何传递外部的表达式(包括方法调用)。
我们知道 "&" 类型的 scope 属性可以绑定来自于父级作用域的表达式,也包括方法调用。
如果在你的 DDO(指令定义对象)里有这样的声明:
javascript
scope: { localFn: '&outerFn' }
那么你可以这样去写指令:<... outer-fn="doSomething(args)">
那么在你的 link
函数内,$scope.localFn
就是一个包装函数,调用此函数将向被绑定的对象发出消息(也就是方法调用)。也就是说,这样写:
javascript
link: function($scope, ...) { $scope.localFn(); }
就等于通过指令去调用来自外部作用域的方法。
最后一个问题就是参数如何传递?很显然这样传参:$scope.localFn(args)
是不行的,因为 $scope.localFn
并不是 doSomething
的直接引用,而是一个包装函数去间接调用 doSomething
。Angular 的解决方式是通过传递一个对象来引用传入的参数(key 对应入参的参数名):
javascript
link: function($scope, ...) { $scope.localFn({args: YOUR_ARGS}); }
解释一下,当 $scope.localFn
被调用时,它首先通过对象参数 {args: YOUR_ARGS}
来匹配目标方法需要的入参,然后再去调用目标方法,类似于:doSomething.call(OUTER_SCOPE, args ...)
,其中 OUTER_SCOPE
自然是 doSomething
所属的作用域对象,这个过程不需要你干预,声明 DDO 的时候 Angular 已经处理的妥妥的了。当然,如果是多个入参,Angular 也会用数组包装一下然后使用 apply
去调用。
这样写指令才能做到之前所说的两点几本要求,即:1)高度可重用;2)低耦合。
巴扎黑2017-04-10 15:00:46
楼主的问题其实是controller与指令的交互。
例如你这里的myDatepicker是一个通用的指令,希望在不同的情境下调用到controller不同的方法。
html代码:
<p ng-controller="testCtrl">
<input my-datepicker id="1" callback="callbackA()"/>
<input my-datepicker id="2" callback="callbackB()"/>
</p>
JavaScript代码:
.directive('myDatepicker', function () {
return {
scope: {
callback: '&'
},
link: function (scope, element, attrs, controller) {
element.datepicker(function () {
scope.callback();
});
}
}
})
.controller('testCtrl', function ($scope, $log) {
$scope.callbackA = function () {
$log.info('callback A');
};
$scope.callbackB = function () {
$log.info('callback B');
}
})