我正在研究一个基本指令,它创建一个适合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>
这也有效,但它很尴尬并且违反了得墨忒耳的法则,这很重要,因为如果我在将来改变其内部工作方式,它可能会打破被抄送的内容。如何改进这一点以避免此问题?
答案 0 :(得分:2)
使用委托模式。我们的想法是在指令中公开一个可自定义的函数,然后插入实际操作。该操作将在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>
并将指令更新为:
scope: {
...
customAction: "&"
},
link: function($scope, $el, $attrs) {
...
$scope.doAction = function(item){
return $scope.customAction(item);
}
}
的 DEMO 强>