3

我正在研究一个基本指令,该指令创建一个适合 Bootstrap 的 div 网格。您给它一个项目集合,并可选择指定它可以包含的列数。它是嵌入的,因此您可以定义为每个项目显示的模板。

我将集合分成行,然后有嵌套的转发器,其中第一个创建每一行,第二个创建每一列(然后嵌入该项目的内容)。它在这个简单的场景中运行良好。

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

这也有效,但它很尴尬并且违反了得墨忒耳法则,这很重要,因为如果我将来在内部改变它的工作方式,它可能会破坏嵌入的内容。我怎样才能改进它以避免这个问题?

功能齐全的 plunk

4

1 回答 1

2

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.

<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

于 2014-03-27T01:52:24.147 回答