2014-03-27 151 views
3

我正在研究一個基本指令,該指令創建適合Bootstrap的div網格。給它一個項目集合,並可以指定它可以包含的列數。它是transcluded,所以你可以定義爲每個項目顯示的模板。AngularJS:在包含嵌套ng重複的transcluded指令中訪問父範圍

我把集合分成行,然後有嵌套的中繼器,第一個創建每一行,第二個創建每列(然後跨越該項目的內容)。它在這種簡單的情況下運作良好。

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

       }); 
      } 
    }; 
    }]); 

問題是在transcluded內容中,我有時需要調用一個函數或從父範圍訪問一個項目。例如,假設我想格式化一個顯示名稱,或添加一個點擊處理程序。

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

因爲這將創建多個transcluded範圍,我已經通過鏈接$parent直到我最終找到它UNNEST範圍。

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

這也適用,但它的笨拙,違反了迪米特,因爲如果我改變是如何工作的內部在未來,它會潛在地打破transcluded內容是重要的法律。我該如何改進以避免此問題?

Fully functional plunk

+0

順便說一句,是你的賬戶是黑客攻擊還是它僅僅是一個ID ......只是好奇? ... :) – zsong

+0

@zsong哈,不。這是對[紅色代碼](http://en.wikipedia.org/wiki/Code_Red_(computer_worm))的引用。 – HackedByChinese

回答

2

使用委託模式。這個想法是在指令中公開一個可定製的函數,並讓真實的行爲被插入。該行爲將在調用函數屬於父範圍的變換範圍內被觸發。

<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

+0

這不違反整個跨越概念,因爲現在transcluded元素依賴於指令的作用域。我只是在大聲思考。 :) – dmahapatro

+0

@dmahapatro基於這個用例,這是一個可能的解決方案,因爲你必須觸及雙方。最好的方法是使用委託模式來清楚地分離邏輯。 – zsong

+0

我曾考慮過這個問題,如果您想要公開一些有限的(和預期的)操作,它肯定會起作用。但是,如果我將此控件通常重複使用,則此解決方案無法分離並修改它,以便爲您所需的操作捅空位。 – HackedByChinese