web-dev-qa-db-ja.com

Angularスコープをng-includeに渡す

次のように、ng-includeおよびng-repeatを使用してアプリの複数の場所で使用するコントローラーを作成しました。

<div
  ng-repeat="item in items"
  ng-include="'item.html'"
  ng-controller="ItemController"
></div>

コントローラー/テンプレートでは、item値が存在することを期待していますが、この考えに基づいて全体が構築されています。しかし、今では、ng-repeatなしで、わずかに異なる方法でコントローラーを使用する必要がありますが、itemを渡すことができる必要があります。 ng-initを見て、次のように、必要なことができると思いました。

<div
  ng-init="item = leftItem"
  ng-include="'item.html'"
  ng-controller="ItemController"
></div>
<div
  ng-init="item = rightItem"
  ng-include="'item.html'"
  ng-controller="ItemController"
></div>

しかし、それは機能していないようです。このような単一のインスタンスでスコープの変数を渡す方法はありますか?

編集:これより上のコントローラーは、次のようなleftItemおよびrightItemの値を読み込んでいます:

.controller('MainController', function($scope, ItemModel) {
    ItemModel.loadItems()
        .then(function(items) {
            $scope.$apply(function() {
                $scope.leftItem = items.left;
                $scope.rightItem = items.right;
            });
        });
});
37
kbjr

私はそれをディレクティブに書き直し、スコープの必要な値を

scope: {
    item: '='
}
17
kbjr

これを行うには、onloadが提供するngInclude属性を使用できます。

<div ng-include="'item.html'"
     ng-controller="ItemController"
     onload="item = rightItem">
</div>

ドキュメントへのリンク

編集

親スコープで次のようなことを試してください:

$scope.dataHolder = {};

次に、非同期データを受信したら、データをdataHolderに保存します。

$scope.dataHolder.leftItem = items.leftItem;
$scope.dataHolder.rightItem = items.rightItem;

ng-includeがテンプレートをロードすると、親のプロパティを継承する子スコープが作成されます。したがって、$scope.dataHolderは、この子スコープで(最初は空のオブジェクトとして)定義されます。ただし、非同期データを受信した場合、空のオブジェクトへの参照には新しく受信したデータが含まれている必要があります。

33
Sunil D.

パーティーに遅れたが、愚かなディレクティブを実装せずにこれを達成するための小さなangular 'hack'がある。

Ng-includeを使用するすべての場所にコントローラーのスコープ(ng-ifなど)を拡張する組み込みディレクティブを追加すると、実際には、含まれているすべてのスコープの変数名を分離できます。

そう:

<div ng-include="'item.html'"
  ng-if="true"
  onload="item = rightItem">
</div>
<div ng-include="'item.html'"
  ng-if="true"
  onload="item = leftItem">
</div>

その後、異なるアイテムを使用して、テンプレートitem.htmlをアイテム変数に複数回バインドできます。

これはあなたが望むものを達成するためのプランカーです

問題は、各オンロード命令で消去されるアイテム変数への参照を1つだけ保持するコントローラースコープでアイテムが変化し続けることでした。

現在のスコープを拡張するディレクティブを導入すると、すべてのng-includeのスコープを分離できます。結果として、アイテム参照は保持され、すべての拡張スコープで一意になります。

32
Piou

@Taninの答えが大好きです。 so一度に多くの問題を非常にエレガントな方法で解決します。 Coffeescriptを知らない私のようなあなたのために、javascriptがあります...

注:私が理解するにはあまりにも新しい理由のため、このコードでは、テンプレート名を二重引用符で囲むng-includeの要件ではなく、テンプレート名onceを引用符で囲む必要があります。 <div ng-include-template="template-name.html" ... >の代わりに<div ng-include-template="'template-name.html'" ... >

.directive('ngIncludeTemplate', function() {  
  return {  
    templateUrl: function(elem, attrs) { return attrs.ngIncludeTemplate; },  
    restrict: 'A',  
    scope: {  
      'ngIncludeVariables': '&'  
    },  
    link: function(scope, elem, attrs) {  
      var vars = scope.ngIncludeVariables();  
      Object.keys(vars).forEach(function(key) {  
        scope[key] = vars[key];  
      });  
    }  
  }  
})
12
Mike

Onloadを使用することは、グローバルスコープを散らかすため、きれいなソリューションではありません。もっと複雑なものがあると、失敗し始めます。

ng-includeはグローバルスコープにアクセスできるため、それほど再利用できません。ちょっと変です。

上記は正しくありません。ng-ifonloadはグローバルスコープを無駄にしません

また、すべての状況に対して特定のディレクティブを記述したくありません。

Ng-includeの代わりに汎用ディレクティブを作成すると、よりクリーンなソリューションになります。

理想的な使用法は次のとおりです。

<div ng-include-template="'item.html'" ng-include-variables="{ item: 'whatever' }"></div>
<div ng-include-template="'item.html'" ng-include-variables="{ item: variableWorksToo }"></div>

ディレクティブは次のとおりです。

.directive(
  'ngIncludeTemplate'
  () ->
    {
      templateUrl: (elem, attrs) -> attrs.ngIncludeTemplate
      restrict: 'A'
      scope: {
        'ngIncludeVariables': '&'
      }
      link: (scope, elem, attrs) ->
        vars = scope.ngIncludeVariables()
        for key, value of vars
          scope[key] = value
    }
)

ディレクティブがグローバルスコープを使用していないことがわかります。代わりに、ng-include-variablesからオブジェクトを読み取り、それらのメンバーを独自のローカルスコープに追加します。

これがあなたの望むものであることを願っています。クリーンで汎用的です。

11
Tanin

これにはng-initの方が良いと思います。

<div ng-include='myFile.html' ng-init="myObject = myCtrl.myObject; myOtherObject=myCtrl.myOtherObject"/>
4
Will Sadler

上記は、<div ng-include-template=... ng-include-variables="{ id: var.id }">などの第2レベルの属性では機能しません。 var.idに注意してください。

更新されたディレクティブ(見苦しいが機能します):

.directive('ngIncludeTemplate', function() {
  return {
    templateUrl: function(elem, attrs) { return attrs.ngIncludeTemplate; },
    restrict: 'A',
    scope: {
      'ngIncludeVariables': '&'
    },
    link: function(scope, elem, attrs) {
      var cache = scope.ngIncludeVariables();
      Object.keys(cache).forEach(function(key) {
        scope[key] = cache[key];
      });

      scope.$watch(
        function() {
          var val = scope.ngIncludeVariables();
          if (angular.equals(val, cache)) {
            return cache;
          }
          cache = val;
          return val;
        },
        function(newValue, oldValue) {
          if (!angular.equals(newValue, oldValue)) {
            Object.keys(newValue).forEach(function(key) {
              scope[key] = newValue[key];
            });
          }
        }
      );

    }
  };
});
3
Viktor Aseev

MikeとTaninの回答の明らかな更新-インラインテンプレートを次のように使用している場合:

<script type="text/ng-template" id="partial">{{variable}}</script>

<div ng-include-template="'partial'" ng-include-variables="{variable: variable}"></div>

次に、ディレクティブngIncludeTemplateで、

templateUrl: function(elem, attrs) { return attrs.ngIncludeTemplate; },  

template: function(elem, attrs) { return document.getElementById(attrs.ngIncludeTemplate.split("'")[1]).innerHTML },
1
Zmey Petrov