Подтвердить что ты не робот

Как правильно привязать область действия между директивой и контроллером с помощью angularJS

Я пытаюсь создать иерархический неупорядоченный список n-уровня с anugularJS и смог успешно это сделать. Но теперь у меня возникают проблемы с областью между директивой и контроллером. Мне нужно изменить свойство scope родителя из функции, вызванной через ng-click в шаблоне директивы.

См. http://jsfiddle.net/ahonaker/ADukg/2046/ - здесь JS

var app = angular.module('myApp', []);

//myApp.directive('myDirective', function() {});
//myApp.factory('myService', function() {});

function MyCtrl($scope) {
    $scope.itemselected = "None";
    $scope.organizations = {
        "_id": "SEC Power Generation",
        "Entity": "OPUNITS",
        "EntityIDAttribute": "OPUNIT_SEQ_ID",
        "EntityID": 2,
        "descendants": ["Eastern Conf Business Unit", "Western Conf Business Unit", "Atlanta", "Sewanee"],
        children: [{
            "_id": "Eastern Conf Business Unit",
            "Entity": "",
            "EntityIDAttribute": "",
            "EntityID": null,
            "parent": "SEC Power Generation",
            "descendants": ["Lexington", "Columbia", "Knoxville", "Nashville"],
            children: [{
                "_id": "Lexington",
                "Entity": "OPUNITS",
                "EntityIDAttribute": "OPUNIT_SEQ_ID",
                "EntityID": 10,
                "parent": "Eastern Conf Business Unit"
            }, {
                "_id": "Columbia",
                "Entity": "OPUNITS",
                "EntityIDAttribute": "OPUNIT_SEQ_ID",
                "EntityID": 12,
                "parent": "Eastern Conf Business Unit"
            }, {
                "_id": "Knoxville",
                "Entity": "OPUNITS",
                "EntityIDAttribute": "OPUNIT_SEQ_ID",
                "EntityID": 14,
                "parent": "Eastern Conf Business Unit"
            }, {
                "_id": "Nashville",
                "Entity": "OPUNITS",
                "EntityIDAttribute": "OPUNIT_SEQ_ID",
                "EntityID": 4,
                "parent": "Eastern Conf Business Unit"
            }]
        }]
    };

    $scope.itemSelect = function (ID) {
        $scope.itemselected = ID;
    }
}

app.directive('navtree', function () {
    return {
        template: '<ul><navtree-node ng-repeat="item in items" item="item" itemselected="itemselected"></navtree-node></ul>',
        restrict: 'E',
        replace: true,
        scope: {
            items: '='
        }
    };
});

app.directive('navtreeNode', function ($compile) {
    return {
        restrict: 'E',
        template: '<li><a ng-click="itemSelect(item._id)">{{item._id}} - {{itemselected}}</a></li>',
        scope: {
            item: "=",
            itemselected: '='
        },
        controller: 'MyCtrl',
        link: function (scope, elm, attrs) {
            if ((angular.isDefined(scope.item.children)) && (scope.item.children.length > 0)) {
                var children = $compile('<navtree items="item.children"></navtree>')(scope);
                elm.append(children);
            }
        }
    };
});

и здесь HTML

<div ng-controller="MyCtrl">
    Selected: {{itemselected}}

    <navtree items="organizations.children"></navtree>
</div>

Обратите внимание, что список создается из модели. И ng-click вызывает функцию для установки свойства родительской области (itemselected), но изменение происходит только локально. Ожидаемое поведение, когда я нажимаю на элемент, заключается в том, что "Selected: None" следует изменить на "Selected: xxx", где xxx - это элемент, который был нажат.

Я не связываю свойство между родительской областью и директивой соответствующим образом? Как передать изменение свойства в родительскую область?

Надеюсь, что это ясно.

Заранее благодарим за помощь.

4b9b3361

Ответ 1

Пожалуйста, взгляните на эту рабочую скрипту, http://jsfiddle.net/eeuSv/

Что я сделал, так это требовать родительский контроллер внутри директивы navtree-node и вызывать функцию-член, определенную в этом контроллере. Функция-член setSelected. Обратите внимание: this.setSelected, а не $scope.setSelected. Затем определите метод navtree-node scope itemSelect. Пока вы нажимаете на теги привязки, он вызывает метод itemSelect в области navtree-node. Этот inturn вызовет метод члена контроллера setSelected, передающий выбранный идентификатор.

scope.itemSelect = function(id){ myGreatParentControler.setSelected(id) }

Ответ 2

Maxdec прав, это связано с областью обзора. К сожалению, это достаточно сложный случай, когда документы AngularJS могут быть неверными для начинающих (например, я).

Предупреждение: скорейся, чтобы я был немного затянутым, когда я пытаюсь объяснить это. Если вы просто хотите увидеть код, перейдите к JSFiddle. Я также нашел видео egghead.io бесценным в изучении AngularJS.

Здесь мое понимание проблемы: у вас есть иерархия директив (navtree, navitem), и вы хотите передать информацию из navitem "вверх по дереву" в корневой контроллер. AngularJS, как и хорошо написанный Javascript в целом, настроен строго на объем ваших переменных, так что вы случайно не испортите другие скрипты, которые также выполняются на странице.

В Angular имеется специальный синтаксис (&), который позволяет вам создать область выделения и вызвать функцию в родительской области:

// in your directive
scope: {
   parentFunc: '&'
}

Пока все хорошо. Все становится сложным, если у вас несколько уровней директив, потому что вы действительно хотите сделать следующее:

  • Имейте функцию в корневом контроллере, который принимает переменную и обновляет модель.
  • Директива среднего уровня
  • Директива дочернего уровня, которая может связываться с корневым контроллером

Проблема заключается в том, что директива дочернего уровня не может видеть корневой контроллер. Я понимаю, что вам нужно настроить "цепочку" в вашей директивной структуре, которая действует следующим образом:

Сначала:. У вас есть функция в корневом контроллере, которая возвращает функцию (которая имеет ссылку на область управления корневым представлением):

$scope.selectFunctionRoot = function () {
    return function (ID) {
        $scope.itemselected = ID;
    }
}

Second: Настройте директиву среднего уровня, чтобы иметь собственную функцию выбора (которая будет передана дочернему элементу), которая возвращает что-то вроде следующего. Обратите внимание, что нам нужно сэкономить область действия директивы среднего уровня, потому что, когда этот код действительно выполняется, он будет в контексте директивы дочернего уровня:

// in the link function of the mid-level directive. the 'navtreelist'
scope.selectFunctionMid = function () {
    // if we don't capture our mid-level scope, then when we call the function in the navtreeNode it won't be able to find the mid-level-scope functions            
    _scope = scope;
    return function (item_id) {
        console.log('mid');
        console.log(item_id);

        // this will be the "root" select function
        parentSelectFunction = _scope.selectFunction();
        parentSelectFunction(item_id);
    };
};

Третий: В директиве child-level (navtreeNode) привязать функцию к ng-click, которая вызывает локальную функцию, которая, в свою очередь, "вызовет цепочку", все путь к корневому контроллеру:

// in 'navtreeNode' link function
scope.childSelect = function (item_id) {
    console.log('child');
    console.log(item_id);

    // this will be the "mid" select function  
    parentSelectFunction = scope.selectFunction();
    parentSelectFunction(item_id);
};

Здесь обновлена ​​вилка вашего JSFiddle, в которой есть комментарии в коде.

Ответ 3

Это может быть потому, что каждая директива создает свою собственную область действия (на самом деле вы говорите им об этом).
Подробнее о директивах вы можете прочитать здесь, особенно в главе "Написание директив (длинная версия)".

область. Если установлено значение:

true - тогда для этой директивы будет создана новая область. Если несколько директив по одному элементу запрашивают новую область, только одна создается новая область. Новое правило области не применяется для корня шаблона, так как корень шаблона всегда получает новый сфера.

{} (хэш объекта) - тогда создается новая область "изоляция". Область "изоляция" отличается от обычного масштаба тем, что она не прототипно наследуется от родительской области. Это полезно, когда создание повторно используемых компонентов, которые не должны изменить данные в родительской области.

Итак, ваши изменения не отражаются в области MyCtrl, потому что каждая директива имеет свою собственную "изолированную" область.

Вот почему клик изменяет только локальную переменную $scope.itemselected, а не "все".