J'ai du mal à utiliser les codes *ngFor
et *ngIf
d'Angular sur le même élément.
Lorsque vous essayez de parcourir la collection dans le *ngFor
, celle-ci est vue sous le nom null
et échoue par conséquent lorsque vous tentez d'accéder à ses propriétés dans le modèle.
@Component({
selector: 'Shell',
template: `
<h3>Shell</h3><button (click)="toggle()">Toggle!</button>
<div *ngIf="show" *ngFor="let thing of stuff">
{{log(thing)}}
<span>{{thing.name}}</span>
</div>
`
})
export class ShellComponent implements OnInit {
public stuff:any[] = [];
public show:boolean = false;
constructor() {}
ngOnInit() {
this.stuff = [
{ name: 'abc', id: 1 },
{ name: 'huo', id: 2 },
{ name: 'bar', id: 3 },
{ name: 'foo', id: 4 },
{ name: 'thing', id: 5 },
{ name: 'other', id: 6 },
]
}
toggle() {
this.show = !this.show;
}
log(thing) {
console.log(thing);
}
}
Je sais que la solution la plus simple consiste à déplacer le *ngIf
vers le haut, mais pour des scénarios tels que le bouclage d'éléments de liste dans un ul
, je finirais avec un li
si la collection est vide, ou mon li
s entouré de conteneurs redondants.
Exemple à cette plnkr .
Notez l'erreur de la console:
EXCEPTION: TypeError: Cannot read property 'name' of null in [{{thing.name}} in ShellComponent@5:12]
Est-ce que je fais quelque chose de mal ou est-ce un bug?
Angular v2 ne prend pas en charge plus d’une directive structurelle sur le même élément.
Comme solution de contournement, utilisez l’élément <ng-container>
qui vous permet d’utiliser des éléments distincts pour chaque directive structurelle, mais il n’apparaît pas dans le DOM.
<ng-container *ngIf="show">
<div *ngFor="let thing of stuff">
{{log(thing)}}
<span>{{thing.name}}</span>
</div>
</ng-container>
<ng-template>
(<template>
avant Angular v4) permet de faire la même chose, mais avec une syntaxe différente qui est source de confusion et n'est plus recommandé
<ng-template [ngIf]="show">
<div *ngFor="let thing of stuff">
{{log(thing)}}
<span>{{thing.name}}</span>
</div>
</ng-template>
Comme tout le monde l'a souligné, même si plusieurs directives de modèle dans un même élément fonctionnent dans angular 1.x, cela n'est pas autorisé dans Angular 2. Vous pouvez trouver plus d'informations à partir d'ici: https://github.com/angular/angular/issues/7315
la solution consiste à utiliser le <template>
comme espace réservé. Le code va comme ceci
<template *ngFor="let nav_link of defaultLinks" >
<li *ngIf="nav_link.visible">
.....
</li>
</template>
mais pour une raison quelconque ci-dessus ne fonctionne pas dans 2.0.0-rc.4
dans ce cas, vous pouvez utiliser cette
<template ngFor let-nav_link [ngForOf]="defaultLinks" >
<li *ngIf="nav_link.visible">
.....
</li>
</template>
Avec les mises à jour, dès maintenant en 2018, angular v6 recommande d'utiliser <ng-container>
au lieu de <template>
alors voici la réponse mise à jour.
<ng-container *ngFor="let nav_link of defaultLinks" >
<li *ngIf="nav_link.visible">
.....
</li>
</ng-container>
Comme @Zyzle l'a mentionné, et @ Günter a mentionné dans un commentaire ( https://github.com/angular/angular/issues/7315 ), cela n'est pas pris en charge.
Avec
<ul *ngIf="show">
<li *ngFor="let thing of stuff">
{{log(thing)}}
<span>{{thing.name}}</span>
</li>
</ul>
il n'y a pas d'éléments <li>
vides lorsque la liste est vide. Même l'élément <ul>
n'existe pas (comme prévu).
Lorsque la liste est remplie, il n'y a aucun élément conteneur redondant.
La discussion github (4792) que @Zyzle mentionnée dans son commentaire présente également une autre solution utilisant <template>
(ci-dessous, j'utilise votre balise d'origine - en utilisant <div>
s):
<template [ngIf]="show">
<div *ngFor="let thing of stuff">
{{log(thing)}}
<span>{{thing.name}}</span>
</div>
</template>
Cette solution n'introduit pas non plus d'éléments de conteneur supplémentaires/redondants.
J'ai une autre solution.
Utilisez [hidden]
au lieu de *ngIf
<div [hidden]="!show" *ngFor="let thing of stuff">
La différence est que *ngIf
supprimera l'élément du DOM, alors que [hidden]
joue réellement avec le style CSS
en définissant display: none
Vous ne pouvez pas avoir ngFor
et ngIf
sur le même élément. Ce que vous pouvez faire est de ne pas remplir le tableau que vous utilisez dans ngFor
jusqu'à ce que l'utilisateur clique sur la bascule de votre exemple.
Voici une méthode de base (pas terrible): http://plnkr.co/edit/Pylx5HSWIZ7ahoC7wT6P
Le tableau ci-dessous répertorie uniquement les éléments pour lesquels la valeur "débutant" est définie . Nécessite à la fois le *ngFor
et le *ngIf
afin d'éviter les lignes indésirables dans html.
A l'origine, *ngIf
et *ngFor
se trouvaient sur la même balise <tr>
, mais ne fonctionnaient pas . Ajout d'un <div>
pour la boucle *ngFor
et placer *ngIf
dans la balise <tr>
, fonctionne comme prévu.
<table class="table lessons-list card card-strong ">
<tbody>
<div *ngFor="let lesson of lessons" >
<tr *ngIf="lesson.isBeginner">
<!-- next line doesn't work -->
<!-- <tr *ngFor="let lesson of lessons" *ngIf="lesson.isBeginner"> -->
<td class="lesson-title">{{lesson.description}}</td>
<td class="duration">
<i class="fa fa-clock-o"></i>
<span>{{lesson.duration}}</span>
</td>
</tr>
</div>
</tbody>
</table>
Cela fonctionnera mais l'élément sera toujours dans le DOM.
.hidden{
display: none;
}
<div [class.hidden]="!show" *ngFor="let thing of stuff">
{{log(thing)}}
<span>{{thing.name}}</span>
</div>
Maintenant, à partir de angular2 beta 8, nous pouvons utiliser *ngIf
et *ngFor
sur le même composant voir ici .
Alterner:
Parfois, nous ne pouvons pas utiliser les balises HTML dans un autre, comme dans tr
, th
(table
) ou dans li
(ul
). Nous ne pouvons pas utiliser une autre balise HTML, mais nous devons effectuer certaines actions dans la même situation pour pouvoir utiliser la balise de fonctionnalité HTML5 <template>
de cette manière.
<template ngFor #abc [ngForOf]="someArray">
code here....
</template>
<template [ngIf]="show">
code here....
</template>
Pour plus d'informations sur les directives structurelles dans angular2 voir ici .
Vous ne pouvez pas utiliser plus d'un Structural Directive
dans Angular sur le même élément, cela crée une confusion et une structure. Vous devez donc les appliquer à deux éléments imbriqués distincts (ou vous pouvez utiliser ng-container
), lisez cette déclaration de l'équipe Angular:
Une directive structurelle par élément hôte
Un jour, vous voudrez peut-être répéter un bloc HTML, mais seulement quand condition particulière est vraie. Vous allez essayer de mettre un * ngFor et un * ngIf sur le même élément Host. Angular ne vous laissera pas faire. Tu peux appliquer une seule directive structurelle à un élément.
La raison est la simplicité. Les directives structurelles peuvent faire des choses complexes avec l'élément Host et ses descendants. Quand deux directives sont posées réclamer le même élément Host, lequel est prioritaire? Lequel devrait aller en premier, le NgIf ou le NgFor? Est-ce que le NgIf peut annuler l'effet du NgFor? Si tel est le cas (et cela semble être le cas), comment devrait-on Angular généraliser la possibilité d'annuler pour autre structurel directives?
Il n'y a pas de réponse facile à ces questions. Interdire plusieurs directives structurelles les rend discutables. Il y a une solution facile pour ce cas d'utilisation: mettez le * ngIf sur un élément conteneur qui enveloppe le * ngFor element. Un ou les deux éléments peuvent être un ng-container afin que vous n’ayez pas à introduire de niveaux supplémentaires de HTML.
Vous pouvez donc utiliser ng-container
(Angular4) comme enveloppe (sera supprimé du dom) ou un div ou un span si vous avez la classe ou d'autres attributs comme ci-dessous:
<div class="right" *ngIf="show">
<div *ngFor="let thing of stuff">
{{log(thing)}}
<span>{{thing.name}}</span>
</div>
</div>
<div *ngFor="let thing of show ? stuff : []">
{{log(thing)}}
<span>{{thing.name}}</span>
</div>
<!-- Since angular2 stable release multiple directives are not supported on a single element(from the docs) still you can use it like below -->
<ul class="list-group">
<template ngFor let-item [ngForOf]="stuff" [ngForTrackBy]="trackBy_stuff">
<li *ngIf="item.name" class="list-group-item">{{item.name}}</li>
</template>
</ul>
en html:
<div [ngClass]="{'disabled-field': !show}" *ngFor="let thing of stuff">
{{thing.name}}
</div>
en css:
.disabled-field {
pointer-events: none;
display: none;
}
Vous ne pouvez pas utiliser plusieurs directives structurelles sur le même élément. Enveloppez votre élément dans ng-template
et utilisez une directive structurelle ici
Vous pouvez également utiliserng-template
(au lieu de template. Voir la remarque relative à l'utilisation du tag template) pour appliquer les deux * ngFor et ngIf au même élément HTML. Voici un exemple où vous pouvez utiliser les deux * ngIf et * ngFor pour le même élément tr dans le tableau angulaire.
<tr *ngFor = "let fruit of fruiArray">
<ng-template [ngIf] = "fruit=='Apple'>
<td> I love apples!</td>
</ng-template>
</tr>
où fruiArray = ['Apple', 'banana', 'mango', 'pineapple']
.
Remarque:
L'utilisation de la balise template
au lieu deng-template
est conditionnée par le fait qu'elle jette StaticInjectionError
à certains endroits.