如果不使用隔离作用域,我似乎无法找到一种方法来从指令内调用父作用域上的函数。我知道如果我使用隔离范围,我可以在隔离中使用“&”来访问父范围上的函数,但是在不需要时使用隔离范围会产生后果。考虑以下 HTML:
<button ng-hide="hideButton()" confirm="Are you sure?" confirm-action="doIt()">Do It</button>
在这个简单的示例中,我想显示一个 JavaScript 确认对话框,并且只有在他们在确认对话框中单击“确定”时才调用 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 不再针对父作用域执行,而是在隔离作用域中执行(因为在任何指令上都使用了隔离作用域导致该元素上的所有指令使用隔离范围)。 Here is a jsFiddle 以上示例中的 ng-hide 不起作用。 (请注意,在这个小提琴中,当您在输入框中键入“yes”时,按钮应该隐藏。)
另一种方法是不使用隔离范围,这实际上是我在这里真正想要的,因为不需要隔离该指令的范围。我唯一的问题是,如果我不在隔离范围上传递它,我如何调用父范围上的方法?
Here is a jsfiddle 我没有使用隔离作用域并且 ng-hide 工作正常,但是,对 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 i.e. '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 设置属性有一个问题,但当你遇到它时,我会让你越过那座桥。
scope:true
)有效,因为该指令根本不会创建新范围,因此您在 link
函数中引用的 scope
属性与之前的“父”范围完全相同。
在父作用域上显式调用 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() 的调用不起作用,我不知道如何使它工作。
首先有一点:在这种情况下,外部控制器的作用域不是指令作用域的父作用域;外部控制器的范围是指令的范围。换句话说,指令中使用的变量名将直接在控制器的范围内查找。
接下来,写 attrs.confirmAction()
不起作用,因为此按钮的 attrs.confirmAction
,
<button ... confirm-action="doIt()">Do It</button>
是字符串"doIt()"
,不能调用字符串,例如"doIt()"()
。
你的问题真的是:
当我将函数名称作为字符串时,如何调用函数?
在 JavaScript 中,您通常使用点表示法来调用函数,如下所示:
some_obj.greet()
但您也可以使用数组表示法:
some_obj['greet']
请注意索引值如何是字符串。因此,在您的指令中,您可以简单地执行以下操作:
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]();
}
});
}
不定期副业成功案例分享
confirm-action="doIt(arg1)"
中指定它们,然后在调用 $eval 之前设置scope.arg1
。但是,使用 $parse: stackoverflow.com/a/16200618/215945 可能会更干净scope.$eval(attrs.confirmAction({arg1: 'blah'})
不起作用。您需要将 $parse 与该语法一起使用。