从AngularJS中没有隔离范围的指令调用控制器函数

时间:2013-07-11 00:10:09

标签: angularjs angularjs-directive angularjs-scope

我似乎无法在不使用隔离范围的情况下从指令中调用父作用域上的函数。我知道如果我使用隔离范围,我可以使用“&”在隔离中访问父作用域上的函数,但在不必要时使用隔离作用域会产生后果。请考虑以下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()的调用不起作用,我不知道如何制作它工作

请注意,我真正想要的答案是如何在不使用隔离范围的情况下调用外部作用域上的函数。并且我不想让这个确认对话框以其他方式工作,因为这个问题的关键是弄清楚如何调用外部作用域,并且仍然可以让其他指令对父作用域起作用。

或者,如果其他指令仍然适用于父作用域,我会有兴趣听到使用隔离范围的解决方案,但我认为这不可行。

4 个答案:

答案 0 :(得分:114)

由于该指令仅调用一个函数(而不是尝试在属性上设置值),因此可以使用$eval而不是$ parse(具有非隔离范围):

scope.$apply(function() {
    scope.$eval(attrs.confirmAction);
});

或者更好,只需使用$apply,这将使$ eval()更改其对范围的参数:

scope.$apply(attrs.confirmAction);

Fiddle

答案 1 :(得分:17)

您想在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设置属性有一个问题,但是当你来到它时我会让你穿过那座桥。

答案 2 :(得分:12)

在父作用域上显式调用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>

答案 3 :(得分:1)

  

我唯一的问题是,如何在父作用域上调用方法   如果我不把它传递给孤立的范围?

     

这是一个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]();
      }
    });
  }