AngularJS: доступ к родительской области во включенной директиве, которая содержит вложенный ng-repeat

Я работаю над базовой директивой, которая создает сетку div, подходящую для Bootstrap. Вы даете ему набор элементов и дополнительно указываете количество столбцов, которые он может содержать. Он включен, поэтому вы можете определить шаблон, отображаемый для каждого элемента.

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

 .directive('grid', [function() {
    return {
            restrict: 'AE',
            template: '<div class="row" ng-repeat="row in rows"><div ng-repeat="item in row" ng-class="{\'col-md-{{columnSpan}}\': true, \'active\': isSelected(item) }"><div class="thumbnail" ng-transclude=""></div></div></div>',
            transclude: true,
            scope: {
                items: '=grid',
                columns: '@',
                columnSpan: '@'
            },
            controller: [
                '$scope', function($scope) {
                }
            ],
            link: function($scope, $el, $attrs) {
                $attrs.$observe('columns', function(val) {
                    $scope.columns = val || 4;
                });

                $attrs.$observe('columnSpan', function(val) {
                    $scope.columnSpan = val || 12 / $scope.columns;
                });

                $scope.$watchCollection('items', function(items) {
                    $scope.rows = $scope.rows || [];
                    $scope.rows.length = 0;

                    if (!items) return;

                    var numRows = Math.floor(items.length / $scope.columns);
                    numRows = items.length % $scope.columns !== 0 ? numRows + 1 : numRows;

                    for (var i = 0; i < numRows; i++) {
                        var row = items.slice(i * $scope.columns, (i + 1) * $scope.columns);
                        $scope.rows.push(row);
                    }

                });
            }
    };
  }]);

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

<!-- addHello is defined on the controller scope. this doesn't work -->
<div grid="items" columns="3">
  {{addHello(item) || 'Undefined'}} (name is {{item.name}})
</div>

Поскольку при этом создается несколько включенных областей, мне приходится отделять область, объединяя $parent в цепочку, пока я в конце концов не найду ее.

<!-- works, but ಠ_ಠ -->
<div grid="items" columns="3">
  {{$parent.$parent.$parent.$parent.addHello(item) || 'Undefined'}} (name is {{item.name}})
</div>

Это также работает, но это неудобно и нарушает закон Деметры, что важно, потому что, если я изменю то, как это работает внутри в будущем, это потенциально нарушит включенный контент. Как я могу улучшить это, чтобы избежать этой проблемы?

Полностью функциональный планк.


person moribvndvs    schedule 27.03.2014    source источник
comment
Кстати, ваш аккаунт действительно взломан или это просто идентификатор?... Просто любопытно... :)   -  person zs2020    schedule 27.03.2014
comment
@zsong Ха, нет. Это ссылка на Code Red   -  person moribvndvs    schedule 27.03.2014


Ответы (1)


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

<div grid="items" columns="3" custom-action="addHello"> //addHello() belongs to the DemoCtrl's scope 
    {{doAction(item) || 'Undefined'}} (name is {{item.name}}) //doAction() belongs to the translcuded scope
</div>

И обновите директиву примерно так:

scope: {
    ...
    customAction: "&"
},
link: function($scope, $el, $attrs) {

    ...

    $scope.doAction = function(item){
        return $scope.customAction(item);
    }
}

ДЕМО

person zs2020    schedule 27.03.2014
comment
разве это не противоречит всей идее включения, потому что теперь включенный элемент зависит от области действия директивы. Я просто думаю вслух. :) - person dmahapatro; 27.03.2014
comment
@dmahapatro Основываясь на этом варианте использования, это одно из возможных решений, поскольку вам нужно касаться обеих сторон. Лучший способ — использовать шаблон делегата для четкого разделения логики. - person zs2020; 27.03.2014
comment
Я думал об этом, и это, безусловно, работает, если у вас есть ограниченный (и ожидаемый) набор действий, которые вы хотите раскрыть. Однако, если я сделаю этот элемент управления многоразовым, это решение будет несостоятельным без его разветвления и модификации, чтобы протыкать дыры для необходимых вам действий. - person moribvndvs; 27.03.2014