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

Jim*_*per 95 angularjs angularjs-directive angularjs-scope

我似乎无法在不使用隔离范围的情况下从指令中调用父作用域上的函数.我知道如果我使用隔离范围,我可以在隔离中使用"&"来访问父范围上的函数,但是在没有必要时使用隔离范围会产生后果.请考虑以下HTML:

<button ng-hide="hideButton()" confirm="Are you sure?" confirm-action="doIt()">Do It</button>
Run Code Online (Sandbox Code Playgroud)

在这个简单的例子中,我想显示一个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();
                }
            });
        }
    };
})
Run Code Online (Sandbox Code Playgroud)

但问题是,因为我使用的是隔离范围,上面示例中的ng-hide不再针对父范围执行,而是在隔离范围内执行(因为在任何指令上使用隔离范围会导致该元素上的所有指令使用孤立的范围).这是上面例子的jsFiddle,其中ng-hide不起作用.(请注意,在此小提琴中,当您在输入框中键入"yes"时,该按钮应隐藏.)

替代方案是不使用隔离范围,这实际上是我真正想要的范围,因为不需要隔离此指令的范围.我唯一的问题是,如果我没有在隔离范围内传递它,如何在父范围上调用方法

这是一个jsfiddle我不使用隔离范围,ng-hide工作正常,但是,当然,对confirmAction()的调用不起作用,我不知道如何使它工作.

请注意,我真正寻找的答案是如何在外部作用域上调用函数而不使用隔离的作用域.我不想以另一种方式使这个确认对话框工作,因为这个问题的关键是要弄清楚如何调用外部作用域,并且仍然能够使其他指令对父作用域起作用.

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

Mar*_*cok 116

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

scope.$apply(function() {
    scope.$eval(attrs.confirmAction);
});
Run Code Online (Sandbox Code Playgroud)

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

scope.$apply(attrs.confirmAction);
Run Code Online (Sandbox Code Playgroud)

小提琴

  • @CMCDragonkai,不,`范围.$ eval(attrs.confirmAction({arg1:'blah'})`将不起作用.你需要使用该语法的$ parse. (3认同)
  • 您将如何在该函数上设置参数? (2认同)
  • @CMCDragonkai,如果函数具有参数,则可以在HTML中以“ confirm-action =“ doIt(arg1)””进行指定,然后在调用$ eval之前设置“ scope.arg1”。但是,使用$ parse可能会更干净:http://stackoverflow.com/a/16200618/215945 (2认同)

Cla*_*Pan 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});
                    });
                }
            });
        }
    };
});
Run Code Online (Sandbox Code Playgroud)

使用$ parse设置属性有一个问题,但是当你遇到它时我会让你穿过那座桥.

  • 没有子作用域(删除`scope:true`)是有效的,因为该指令根本不会创建一个新的作用域,因此你在`link`函数中引用的`scope`属性与'parent'作用域的作用域完全相同先前. (2认同)

Jas*_*son 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>
Run Code Online (Sandbox Code Playgroud)