J'ai écrit un contrôleur que j'utilise à plusieurs endroits de 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, la valeur item
doit exister, et l’ensemble est construit autour de cette idée. Maintenant, cependant, je dois utiliser le contrôleur d’une manière légèrement différente, sans le ng-repeat
, Mais je dois tout de même pouvoir passer un item
. J'ai vu ng-init
Et j'ai pensé qu'il 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 une idée sur la façon dont je peux transmettre une variable pour scope dans un cas singulier comme celui-ci?
Edit: Le contrôleur au-dessus de cela charge 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;
});
});
});
J'ai fini par le réécrire dans une directive et lier la valeur nécessaire dans le champ d'application avec
scope: {
item: '='
}
Vous pouvez utiliser l'attribut onload
que ngInclude
fournit pour cela:
<div ng-include="'item.html'"
ng-controller="ItemController"
onload="item = rightItem">
</div>
[~ # ~] éditer [~ # ~]
Essayez de faire quelque chose comme ceci dans la portée parent:
$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, quand ng-include
charge le modèle, il créera une étendue enfant qui héritera des propriétés du parent. Alors $scope.dataHolder
sera défini dans cette étendue enfant (initialement sous forme d'objet vide). Mais lorsque vos données asynchrones sont reçues, la référence à l'objet vide doit alors contenir les données nouvellement reçues.
En retard pour le parti, mais il y a un peu angular 'bidouillage' pour y parvenir sans mettre en œuvre une directive idiote.
L'ajout d'une directive intégrée qui étendra la portée de votre contrôleur (comme ng-if) partout où vous utilisez ng-include vous permettra d'isoler le nom de la variable pour toutes les portées 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 modèle item.html à la variable d'élément à plusieurs reprises avec différents éléments.
Voici un plunker pour réaliser ce que vous voulez
Le problème était que l'élément continuait à changer dans la portée du contrôleur et qu'il ne contenait qu'une référence à une variable d'élément qui était effacée à chaque instruction onload.
L'introduction d'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 à l'article est préservée et unique dans toute la portée étendue.
LOVE @ La réponse de Tanin. résout donc beaucoup de problèmes à la fois et de manière très élégante. Pour ceux d'entre vous comme moi qui ne connaissent pas Coffeescript, voici le javascript ...
REMARQUE: Pour des raisons qui me semblent trop nouvelles pour être comprises, ce code nécessite que vous citiez le nom de votre modèle ne fois, plutôt que l'obligation de ng-include de citer deux fois vos noms de modèles, 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 charge n'est pas une solution propre, car elle a une portée globale. Si vous avez quelque chose de plus complexe, ça 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 n'éclaire 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 voudriez; c'est propre et générique.
ng-init est préférable 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 }">
. Remarquez 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 intégrés comme:
<script type="text/ng-template" id="partial">{{variable}}</script>
<div ng-include-template="'partial'" ng-include-variables="{variable: variable}"></div>
Ensuite, dans la directive ngIncludeTemplate, remplacez
templateUrl: function(elem, attrs) { return attrs.ngIncludeTemplate; },
Avec
template: function(elem, attrs) { return document.getElementById(attrs.ngIncludeTemplate.split("'")[1]).innerHTML },