2014-07-10 154 views
2

我正在構建一個指令,可以在不同的控制器中使用,我希望能夠將指令綁定到我的$scope的特定屬性。AngularJS:指示指令的範圍

我願做這樣的事情:

<div ng-app="myapp"> 
    <div ng-controller="myController"> 
     <my-directive-wrapper ng-model="mymodel"> 
      <my-directive-inner ng-repeat="item in items" /> 
     </my-directive-wrapper> 
    </div> 
</div> 

在這種模式下:

$scope.mymodel = { 
    name : "Transclude test", 
    items : [ 
     { title : "test1" }, 
     { title : "test2" }, 
     { title : "test3" } 
    ] 
}; 

所以該指令myDirectiveWrapper得到$scope.mymodel的範圍,而不是其他。然後我可以將指令放兩遍,每個指向一個不同的屬性。

我這兒有問題的演示項目:http://jsfiddle.net/vtortola/P8JMm/3/

而同樣的演示正常工作(不限制範圍)位置:http://jsfiddle.net/vtortola/P8JMm

的問題是,如何在使用說明我的指令,我想使用我的$scope的特定屬性作爲我的指令的範圍。應該可以將該指令綁定到相同的$scope中的任意屬性。

乾杯。

+0

你看過傳遞給鏈接函數參數的'$ transclude'函數嗎?它允許你傳遞一個對象作爲被轉義部分的作用域和一個DOM替換函數。結帳這個SO線程http://stackoverflow.com/questions/13183005/what-exactly-do-you-do-with-the-transclude-function-and-the-clone-linking-functi – javaCity

+1

這是如何http:///jsfiddle.net/P8JMm/4/ –

+0

@NidhishKrishnan變量名沒有被綁定,它應該在屏幕上顯示「Transclude test」。該指令使用$ scope,而不僅僅是它的一部分。好主意,但。 – vtortola

回答

2

因此,此問題的基本答案是 - 您可以執行您想要執行的操作,但它比你想象的要複雜一點。要了解這裏發生的情況,您需要了解有關角度範圍的知識。範圍本質上是一個包含視圖可訪問的數據的對象。有(至少)三種示波器在角度上運行:

  • 隔離 - 在這種情況下,角度基本上爲指令創建了一個全新的範圍。沒有任何屬性被複制。
  • 擴展 - 在這種情況下,您將從根作用域開始,但對其進行淺層複製。被改變的對象將在根範圍內被改變,但基元不會被改變。
  • 共享 - 在這種情況下,您可以與根作用域共享部分甚至全部數據。

根據你上面的問題,你在這裏做什麼是擴展父範圍 - 複製一個對象到新創建的子範圍中具有特定名稱的屬性。獲得此行爲的方法是在transclude之前手動創建新的子範圍。代碼的兩個關鍵的線要做到這一點是:

// create a "new" scope 
var childScope = scope.$new(); 

// extend using the model binding provided 
angular.extend(childScope, scope[iAttr.myModel]); 

在你的指令的情況下,這看起來像:

.directive('myDirectiveWrapper', ['$compile', function ($compile) { 
    return { 
     transclude: true, 
     restrict: 'E', 
     compile: function (element, attrs, transclude) { 
      var contents = element.contents().remove(); 
      var compiledContents; 
      return function(scope, iElement, iAttr) { 
       var childScope = scope.$new(); 
       angular.extend(childScope, scope[iAttr.myModel]); 
       if (!compiledContents) { 
        compiledContents = $compile(contents, transclude); 
       } 
       compiledContents(childScope, function(clone, childScope) { 
         iElement.append(clone); 
       }); 
      }; 
     }, 
     template: "<div><h3>{{ name }}</h6><a class='back'>Back</a><div ng-transclude class='list'></div><a class='next'>Next</a>" 
    } 
}]) 

現在,您可以指定要作爲「樣板」的任何變量子範圍,然後你可以直接訪問你的transcluded代碼的內容!

查閱小提琴:http://jsfiddle.net/P8JMm/7/

編輯:只是爲了好玩,我創建了這個指令較爲複雜的情況下:http://jsfiddle.net/P8JMm/9/

- 角部位也有一些非常好的更好地理解範圍的資源。 See here

+1

當你不能'angular.extend'時,它會複製mymodel上的所有原語,這樣就會中斷雙向綁定。 – rob

+0

@rob你是對的。爲此,OP需要分配一個特定的變量,或者需要使用對象進行雙向綁定而不是基元。不幸的是,無論採用何種方法都存在限制... –

1

如果您希望雙向綁定工作,只需在您的指令範圍上創建一個變量,而不是直接將mymodel應用於指令範圍,就會容易得多。

HTML

<div ng-app="myapp"> 
    <div ng-controller="myController"> 
     <my-directive-wrapper model="mymodel"> 
      <my-directive-inner ng-repeat="item in mymodel.items" /> 
     </my-directive-wrapper> 
    </div> 
</div> 

指令

.directive("myDirectiveWrapper", function(){ 
    return { 
     scope: { 
      model: '='   
     }, 
     restrict: 'E', 
     transclude: true, 
     link: function(scope, element, attrs, controller) { 

     }, 
     template: "<div><h3>{{ model.name }}</h6><a class='back'>Back</a><div ng-transclude class='list'></div><a class='next'>Next</a>" 
    } 
}) 

http://jsfiddle.net/kQ4TV/

如果你不關心雙向綁定我想你可以做這樣的事情,但我不會不推薦:

.directive("myDirectiveWrapper", function(){ 
    return { 
     scope: { 
      model: '='   
     }, 
     restrict: 'E', 
     transclude: true, 
     link: function(scope, element, attrs, controller) { 
      angular.extend(scope, scope.model); 
     }, 
     template: "<div><h3>{{ name }}</h6><a class='back'>Back</a><div ng-transclude class='list'></div><a class='next'>Next</a>" 
    } 
}) 

http://jsfiddle.net/vWftR/

下面是當第二種方法可能會導致問題的例子。請注意,當您在輸入字段中輸入內容時,它將更改指令的名稱,但不會更改外部範圍中的名稱:http://jsfiddle.net/r5JeJ/

+0

您的解決方案也不錯,但@ drew_w的解決方案更接近我想要的,儘管存在綁定問題。 – vtortola