Portée de passage angulaire à ng-include

J'ai un contrôleur que j'ai écrit que j'utilise dans de multiples endroits dans mon application avec ng-include et ng-repeat , comme ceci:

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

Dans le contrôleur / modèle, je m'attends à ce que la valeur de l' item existe, et tout est construit autour de cette idée. Maintenant, cependant, je dois utiliser le contrôleur de manière légèrement différente, sans la ng-repeat , mais je dois toujours pouvoir passer un item . J'ai vu ng-init et pensé que cela pourrait faire ce dont j'avais besoin, comme ceci:

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

Mais cela ne semble pas fonctionner. Quelqu'un a-t-il des idées sur la façon dont je peux passer dans une variable de portée dans une instance singulière comme celle-ci?

Modifier: Le contrôleur ci-dessus est chargé dans les valeurs leftItem et rightItem , quelque chose comme ceci:

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

Vous pouvez utiliser l'attribut onload que ngInclude fournit pour effectuer ceci:

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

Lien vers la documentation .

MODIFIER

Essayez de faire quelque chose comme ça dans la portée parentale:

 $scope.dataHolder = {}; 

Ensuite, lorsque les données asynchrones sont reçues, stockez les données sur dataHolder :

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

Maintenant, lorsque ng-include charge le modèle, il créera une portée enfant qui hérite des propriétés du parent. Ainsi, $scope.dataHolder sera défini dans cette portée enfant (initialement comme un objet vide). Mais lorsque vos données asynchrones sont reçues, la référence à l'objet vide devrait alors contenir les données nouvellement reçues.

À la fin de la fête, il y a un petit «hack» angulaire pour y parvenir sans mettre en œuvre une directive stupide.

L'ajout d'une directive intégrée qui étendra la portée de votre contrôleur (comme ng-if) partout où vous utilisez la ng-include vous permettra d'isoler le nom de la variable pour toutes les étendues incluses.

Alors:

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

Vous pouvez ensuite lier votre template item.html à l'élément variable à plusieurs reprises avec différents éléments.

Voici un pommier pour atteindre ce que vous voulez

Le problème était que l'élément change toujours dans la portée du contrôleur qui ne contient qu'une référence à la variable d'élément qui est effacée à chaque instruction de chargement.

En présentant une directive qui étend la portée actuelle, vous permet d'avoir une portée isolée pour tous les ng-include. En conséquence, la référence de l'élément est conservée et unique dans toute la portée étendue.

J'ai fini par la réécrire dans une directive et liant la valeur nécessaire dans la portée avec

 scope: { item: '=' } 

LOVE @ réponse de Tanin. Résout tant de problèmes à la fois et de manière très élégante. Pour ceux d'entre vous qui m'aiment qui ne connaissent pas Coffeescript, voici le javascript …

REMARQUE: pour des raisons, je suis trop nouveau pour comprendre, ce code vous oblige à citer votre nom de modèle une fois , plutôt que l'exigence de ng-include de citer deux fois le nom de votre modèle, c'est-à-dire. <div ng-include-template="template-name.html" ... > au lieu de <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]; }); } } }) 

L'utilisation de la recharge n'est pas une solution propre car elle gagne la portée globale. Si vous avez quelque chose de plus complexe, cela va commencer à échouer.

Ng-include n'est pas réutilisable car il a accès à la portée globale. C'est un peu bizarre.

Ce qui précède n'est pas vrai. Ng-si avec onload ne jette pas la portée globale

Nous ne voulons pas non plus écrire une directive spécifique pour chaque situation.

Faire une directive générique au lieu de ng-include est une solution plus propre.

L'utilisation idéale ressemble à:

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

La directive est:

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

Vous pouvez voir que la directive n'utilise pas la portée globale. Au lieu de cela, il lit l'objet à partir de ng-include-variables et ajoute ces membres à sa propre portée locale.

J'espère que c'est ce que vous voulez; C'est propre et générique.

Ng-init est mieux pour cela je pense.

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

Ci-dessus ne fonctionnera pas pour les attributs de second niveau, comme <div ng-include-template=... ng-include-variables="{ id: var.id }"> . Notez le var.id

Directive mise à jour (moche, mais fonctionne):

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

Peut-être que la mise à jour évidente pour Mike et Tanin répond – si vous utilisez des modèles en ligne comme:

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

Ensuite, dans directive ngIncludeTemplate, remplacer

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

Avec

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