简体   繁体   English

AngularJS:在包含嵌套ng-repeat的transcluded指令中访问父作用域

[英]AngularJS : Accessing parent scope in a transcluded directive that contains nested ng-repeat

I am working on a basic directive that creates a grid of divs that fits with Bootstrap. 我正在研究一个基本指令,它创建一个适合Bootstrap的div网格。 You give it a collection of items and optionally specify the number of columns it can contain. 您为它提供了一组项目,并可选择指定它可以包含的列数。 It is transcluded so you can define the template displayed for each item. 它被转换,因此您可以定义为每个项目显示的模板。

I break the collection into rows, and then have nested repeaters where the first creates each row, and the second creates each column (and then transcludes the content for that item). 我将集合分成行,然后嵌套的中继器,第一个创建每一行,第二个创建每个列(然后转换该项的内容)。 It works well in this simple scenario. 它在这个简单的场景中运行良好。

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

                });
            }
    };
  }]);

The problem is in the transcluded content, I sometimes need to invoke a function or access an item from the parent scope. 问题在于被转换的内容,我有时需要调用一个函数或从父作用域访问一个项目。 For example, say I want to format a display name, or add a click handler. 例如,假设我要格式化显示名称,或添加单击处理程序。

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

Because this creates multiple transcluded scopes, I have to unnest the scope by chaining $parent until I eventually find it. 因为这会创建多个被转换的范围,所以我必须通过链接$parent来取消范围,直到我最终找到它。

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

This also works, but it's awkward and violates law of Demeter, which is important because if I change how this works internally in the future, it'd potentially break the transcluded content. 这也有效,但它很尴尬并且违反了得墨忒耳的法则,这很重要,因为如果我在将来改变其内部工作方式,它可能会打破被抄送的内容。 How can I improve this to avoid this issue? 如何改进这一点以避免此问题?

Fully functional plunk . 功能齐全的插头

Use delegate pattern. 使用委托模式。 The idea is to expose a customizable function in the directive and let the real action to be plugged in. The action will be triggered inside the transcluded scope calling the function belongs to the parent scope. 我们的想法是在指令中公开一个可自定义的函数,然后插入实际操作。该操作将在transcluded范围内触发,调用该函数属于父范围。

<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>

And update the directive to something like this: 并将指令更新为以下内容:

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

    ...

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

DEMO DEMO

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM