在AngularJS中调用一个没有独立范围的指令的控制器函数
我似乎无法find一种方法来调用父指定范围内的function,而不使用隔离范围。 我知道,如果我使用隔离作用域,我可以在隔离中使用“&”来访问父作用域上的函数,但是在不需要时使用隔离作用域会产生后果。 考虑下面的HTML:
<button ng-hide="hideButton()" confirm="Are you sure?" confirm-action="doIt()">Do It</button>
在这个简单的例子中,我想显示一个JavaScript确认对话框,只有在确认对话框中点击“OK”才能调用doIt()。 使用隔离范围很简单。 该指令看起来像这样:
.directive('confirm', function () { return { restrict: 'A', scope: { confirm: '@', confirmAction: '&' }, link: function (scope, element, attrs) { element.bind('click', function (e) { if (confirm(scope.confirm)) { scope.confirmAction(); } }); } }; })
但问题是,因为我使用的是独立作用域,所以上例中的ng-hide不再针对父作用域执行 ,而是在独立作用域中执行(因为在任何指令上使用隔离作用域都会导致该元素的所有指令使用隔离的范围)。 上面这个示例中的一个jsFiddle ,其中的ng-hide不起作用。 (请注意,在这个小提琴中,当您在input框中input“是”时,该button应该隐藏)。
另一种select是不使用隔离的范围 ,这实际上是我真正想要的,因为这个指令的范围不需要被隔离。 我唯一的问题是, 如果我不在隔离范围内传递它 , 如何在父范围上调用方法 ?
这里是一个jsfiddle ,我不使用独立的范围和ng-hide工作正常,但是,当然,confirmAction()调用不起作用,我不知道如何使它工作。
请注意,我真正想要的答案是如何调用外部作用域的函数,而不使用隔离的作用域。 而且我不希望以另一种方式使这个确认对话框工作,因为这个问题的关键是要弄清楚如何调用外部范围,还能够使其他指令对父范围起作用。
另外,如果其他指令仍然对父范围起作用,那么我会有兴趣听到使用隔离范围的解决scheme ,但我认为这是不可能的。
由于指令只是调用一个函数(而不是试图设置一个属性的值),你可以使用$ eval而不是$ parse(带有非隔离的作用域):
scope.$apply(function() { scope.$eval(attrs.confirmAction); });
或者更好的是,只需使用$ apply ,$ eval()将它的参数用作范围:
scope.$apply(attrs.confirmAction);
小提琴
你想在AngularJS中使用$ parse服务。
所以对于你的例子:
.directive('confirm', function ($parse) { return { restrict: 'A', // Child scope, not isolated scope : true, link: function (scope, element, attrs) { element.bind('click', function (e) { if (confirm(attrs.confirm)) { scope.$apply(function(){ // $parse returns a getter function to be // executed against an object var fn = $parse(attrs.confirmAction); // In our case, we want to execute the statement in // confirmAction ie 'doIt()' against the scope // which this directive is bound to, because the // scope is a child scope, not an isolated scope. // The prototypical inheritance of scopes will mean // that it will eventually find a 'doIt' function // bound against a parent's scope. fn(scope, {$event : e}); }); } }); } }; });
使用$ parse设置一个属性有一个问题,但是当你到达它时,我会让你跨过这个桥。
在父范围上显式调用hideButton
。
这是小提琴: http : //jsfiddle.net/pXej2/5/
这里是更新的HTML:
<div ng-app="myModule" ng-controller="myController"> <input ng-model="showIt"></input> <button ng-hide="$parent.hideButton()" confirm="Are you sure?" confirm-action="doIt()">Do It</button> </div>
我唯一的问题是,如果我不在隔离范围内传递它,如何在父范围上调用方法?
这里是一个jsfiddle,我不使用独立的范围和ng-hide工作正常,但是,当然,confirmAction()调用不起作用,我不知道如何使它工作。
首先是一个小点:在这种情况下,外部控制者的范围不是指令范围的父范围; 外部控制器的范围是指令的范围。 换句话说,指令中使用的variables名将直接在控制器的作用域中查找。
接下来,写attrs.confirmAction()
不起作用,因为attrs.confirmAction
这个button,
<button ... confirm-action="doIt()">Do It</button>
是string"doIt()"
,你不能调用一个string,例如"doIt()"()
。
你的问题确实是:
当我有一个string的名字时,如何调用一个函数?
在JavaScript中,你大多使用点符号来调用函数,如下所示:
some_obj.greet()
但是你也可以使用数组符号:
some_obj['greet']
请注意索引值是一个string 。 所以,在你的指令中,你可以简单地做到这一点:
link: function (scope, element, attrs) { element.bind('click', function (e) { if (confirm(attrs.confirm)) { var func_str = attrs.confirmAction; var func_name = func_str.substring(0, func_str.indexOf('(')); // Or func_str.match(/[^(]+/)[0]; func_name = func_name.trim(); console.log(func_name); // => doIt scope[func_name](); } }); }