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

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

问题描述

我有这个指令:

app.directive('recursiveListItem', ['$http', 'RecursionHelper', function ($http, RecursionHelper) {返回 {限制:'E',范围: {父母:'=',onNodeClick: '&',},编译:函数(元素,属性){返回 RecursionHelper.compile(element);},模板:'<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>\

\<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>\\</ul>',};}]);

这里是帮手:

app.factory('RecursionHelper', ['$compile', function ($compile) {var RecursionHelper = {编译:函数(元素){var 内容 = element.contents().remove();var 编译内容;返回函数(范围,元素){如果(!编译内容){编译内容 = $compile(contents);}编译内容(范围,功能(克隆){element.append(克隆);});};}};返回递归助手;}]);

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

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

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

解决方案

潜在问题

作为引导,它有助于查看&"docs 是这样描述的:

<块引用>

&或 &attr - 提供一种在上下文中执行表达式的方法父作用域.

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

来获取金额值

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

函数(节点){console.log("用"调用,节点);}

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

function (locals) {返回 parentGet(scope, locals);}

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

问题是,据我所知,当我们嵌套该作用域变量时,它会不断更新到新的嵌套父作用域.那对我们不利.我们希望执行上下文是您的点击处理程序所在的顶级范围.否则,我们将失去对您的功能的引用 - 正如我们所看到的那样.

解决方案

为了解决这个问题,我们保留了对原始函数的引用(当我们嵌套时,它不受 parentGet() 的影响).然后当我们传入时,parentGet() 中使用的作用域就是上面有点击处理函数的作用域.

首先让我们为实际函数使用一个不同于参数的名称.我像这样设置函数 $scope.onNodeClickFn = function(node) {...}

然后我们进行 3 处更改:

1) 添加第二个作用域变量 (topFunc):

范围:{onNodeClick: '&',顶函数:'='}

onNodeClick 是封装的函数(包括作用域参数).而 topFunc 是对解包函数的引用(请注意,我们使用 = 传递了它,因此它是对没有 & 包装器的函数的引用代码>适用).

2) 在顶层,我们传入两个函数引用.

(注意 top-func 参数中缺少 ().)

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

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

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

你可以看到它在这个小提琴中工作: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天全站免登陆