如何在AngularJS的递归指令中传递函数引用? [英] How to pass a function reference in a recursive directive in AngularJS?

查看:85
本文介绍了如何在AngularJS的递归指令中传递函数引用?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有此指令:

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)",但这也不起作用.

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

解决方案

基本问题

作为线索有助于研究&" 文档描述了这种方式:

&或& attr-提供一种在以下情况下执行表达式的方法 父范围.

通常希望通过 表达式和父作用域,这可以通过传递映射来完成 局部变量名和值放入表达式包装器fn中.为了 例如,如果表达式是增量(金额),那么我们可以指定 通过将localFn调用为localFn({amount:22})

为了实现此目的,将通过&传递的函数包装在另一个函数parentGet()中.通过查看点击处理程序函数变量的内容,我们可以看到这一点.首先,在传递给&之前,它与我们期望的一样:

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

但是,然后,在您的指令中,在通过'&'之后,它看起来像这样:

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/

I have this 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>',     
    };
}]);

and here is the helper:

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;
}]);

Everything works like a charm, but I don't get my on-node-click to work. For the all the root items the 'callback' works fine, but from there on, all the children won't fire the callback. I think it has something to do with passing the function reference to the next child in the template.

I've also tried data-on-node-click="onNodeClick(node)", but that doesn't work either.

Does someone know how to pass the function reference to the child nodes?

解决方案

Underlying Issue

As a lead in its helpful to look at '&' which the docs describe this way:

& or &attr - provides a way to execute an expression in the context of the parent scope.

Often it's desirable to pass data from the isolated scope via an expression and to the parent scope, this can be done by passing a map of local variable names and values into the expression wrapper fn. For example, if the expression is increment(amount) then we can specify the amount value by calling the localFn as localFn({amount: 22})

In order to achieve this the function that is passed through an & is wrapped in another function parentGet(). We can see this in action by looking at the contents of the click handler function variable. First, before it's passed into the & it's as we'd expect:

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

But, then, inside your directive, after passing through '&', it looks like this:

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

So instead of a direct reference to your click handler function it is now a call that will apply your function using both the parent scope and any locals which you pass in.

The problem is that, from what I can tell, as we nest down that scope variable keeps getting updated to the new nested parent scope. That's not good for us. We want the execution context to be that top scope where your click handler is. Otherwise we lose reference to your function- as we see happening.

Solution

To fix this we keep a reference to the original function (that isn't encumbered by parentGet() as we nest down). Then when we pass it in, the scope used in parentGet() is the one with click handler function on it.

First let's use a different name for the actual function than you'll use for the parameter. I set up the function like so $scope.onNodeClickFn = function(node) {...}

Then we make 3 changes:

1) Add a second scope variable (topFunc):

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

onNodeClick is the wrapped function (including scope parameter). And topFunc is a reference to the unwrapped function (note that we pass that in using = so it's a reference to the function absent the wrapper that & applies).

2) At the top level, we pass both function references in.

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

(note the lack of () on the top-func parameter.)

3) Pass the new topFunc parameter in to the template:

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

So we are now maintaining a reference, at each nested scope, to the original function and using that in the template.

You can see it working in this fiddle: http://jsfiddle.net/uf6Dn/9/

这篇关于如何在AngularJS的递归指令中传递函数引用?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆