如何在Angular中的递归指令中传递函数引用?

时间:2013-11-19 13:49:57

标签: angularjs angularjs-directive

我有这个指令:

app.directive('recursiveListItem', ['$http', 'RecursionHelper', function ($http, RecursionHelper) {
    return {
        restrict: 'E',
        scope: {
            parent: '=',
            onNodeClick: '&',
        },
        compile: function (element, attributes) {
            return RecursionHelper.compile(element);
        },
        template:
            '<div class="list-group-item-heading text-muted parent "> \
                <input type="checkbox" data-ng-click="visible = !visible" id="{{parent.Name}}">\
                <label for="{{parent.Name}}">&nbsp;&nbsp;</label>\
                <a href="javascript:void(0)" data-ng-click="onNodeClick({node: parent})">{{parent.Name}}</a> \
            </div> \
            <ul data-ng-if="parent.Children.length > 0" data-ng-show="visible"> \
                <li ng-repeat="child in parent.Children"> \
                    <recursive-list-item data-parent="child" data-on-node-click="onNodeClick"></recursive-list-item> \
                </li> \
            </ul>',     
    };
}]);

这是帮手:

app.factory('RecursionHelper', ['$compile', function ($compile) {
    var RecursionHelper = {
        compile: function (element) {
            var contents = element.contents().remove();
            var compiledContents;
            return function (scope, element) {
                if (!compiledContents) {
                    compiledContents = $compile(contents);
                }
                compiledContents(scope, function (clone) {
                    element.append(clone);
                });
            };
        }
    };

    return RecursionHelper;
}]);

一切都像魅力一样,但我没有让on-node-click工作。对于所有根项目,'回调'工作正常,但从那里开始,所有孩子都不会触发回调。我认为这与将函数引用传递给模板中的下一个子项有关。

我也尝试了data-on-node-click="onNodeClick(node)",但这也不起作用。

有人知道如何将函数引用传递给子节点吗?

1 个答案:

答案 0 :(得分:23)

基础问题

作为有助于了解'&amp;'的主角docs以这种方式描述:

  

&安培;或&amp; attr - 提供在上下文中执行表达式的方法   父范围。

     

通常需要通过以下方式从隔离范围传递数据   表达式和父作用域,这可以通过传递一个映射来完成   将局部变量名和值放入表达式包装器fn中。对于   例如,如果表达式是增量(金额),那么我们可以指定   通过将localFn称为localFn({amount:22})

来计算金额值

为了实现这一点,通过&传递的函数包含在另一个函数parentGet()中。我们可以通过查看click handler function变量的内容来看到这一点。首先,在它传递到&之前,正如我们所期望的那样:

function (node) {
    console.log("called with ",node);
} 

然而,在你的指令中,经过'&amp;'后,它看起来像这样:

function (locals) {
     return parentGet(scope, locals);
} 

因此,不是直接引用您的点击处理函数,而是现在调用将使用父作用域和您传入的任何locals 来应用您的函数。

问题是,从我所知道的,当我们嵌套时,范围变量不断更新到新的嵌套父范围。这对我们不利。我们希望执行上下文成为您的单击处理程序所在的顶级作用域。否则,我们会失去对您的功能的参考 - 正如我们所看到的那样。

解决方案

为了解决这个问题,我们保留对原始函数的引用(在我们嵌套时不会被parentGet()阻碍)。然后当我们传入它时,parentGet()中使用的范围就是具有点击处理函数的范围。

首先让我们使用与实际功能不同的名称,而不是用于参数。我设置了像$scope.onNodeClickFn = function(node) {...}

这样的函数

然后我们进行了3次更改:

1)添加第二个范围变量(topFunc):

scope: {
          onNodeClick: '&',
          topFunc: '='
       }

onNodeClick是包装函数(包括范围参数)。 topFunc是对未展开函数的引用(请注意,我们在使用=时传递了它,因此它是对缺少&适用的包装器的函数的引用。

2)在顶层,我们传递了两个函数引用。

<recursive-list-item on-node-click="onNodeClickFn(node)" top-func="onNodeClickFn" parent=parent ></recursive-list-item>

(注意top-func参数缺少()。)

3)将新的topFunc参数传递给模板:

<recursive-list-item data-parent="child" data-on-node-click="topFunc(node)" top-func="topFunc"></recursive-list-item> \

所以我们现在在每个嵌套范围内维护一个引用原始函数并在模板中使用

你可以看到它在这个小提琴中起作用:http://jsfiddle.net/uf6Dn/9/