Как передать ссылку на функцию в рекурсивной директиве в Angular?

у меня есть такая директива:

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)", но это не сработало.

тут кто-то знает, как передать ссылку на функцию дочерним узлам?

2 ответов


Главная Проблема

в качестве руководства в его полезно посмотреть на"&", который docs описать таким образом:

& или & attr-предоставляет способ выполнения выражения в контексте родительская область.

часто желательно передавать данные из изолированной области через выражение и в родительскую область, это можно сделать, передав карту имен локальных переменных и значений в оболочку выражения fn. Для например, если выражение increment (сумма), то мы можем указать значение суммы путем вызова 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 - это обернутая функция (включая параметр scope). И topFunc является ссылкой на развернутую функцию (обратите внимание, что мы передаем это при использовании = таким образом, это ссылка на функцию, отсутствующую в оболочке, которая & применяется).

2) в верхний уровень, мы передаем обе ссылки на функции.

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

(обратите внимание на отсутствие () на


Я просто делаю то же самое с приложением анкеты, и то, как я сохраняю функцию, передаваемую в изолированной области через все уровни, помещает функцию в каждый элемент в функции({param:param}) нотация

в вашем может быть также:

 <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({param:param})"></recursive-list-item> \
            </li> \
        </ul>',  

от главного контроллера (страница, на которой добавляется директива) onNodeClick имеет параметры, которые будут установлены в директиве, вы должны указать имя в атрибуте on-node-click корневая директива, что-то вроде этого

on-node-click="onNodeClick(param1,param2)"

и в контроллере директивы вы также можете вызвать его:

onNodeClick({param1: value1, param2:value2})

В противном случае, если onNodeClick влияет только на vars в главном контроллере(страница, на которой добавляется директива), вы можете передать директиве имя функции с параметром, существующим в области главного контроллера, и использовать его без параметров в контроллере директивы.