* ngIf und * ngFor auf demselben Element verursachen Fehler

473

Ich habe ein Problem mit dem Versuch, Angulars *ngForund *ngIfdasselbe Element zu verwenden.

Beim Versuch, die Sammlung in der zu durchlaufen *ngFor, wird die Sammlung als angezeigt nullund schlägt folglich fehl, wenn versucht wird, auf ihre Eigenschaften in der Vorlage zuzugreifen.

@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);
  }

}

Ich weiß, dass die einfache Lösung darin besteht, die *ngIfEbene nach oben zu verschieben, aber für Szenarien wie das Durchlaufen von Listenelementen in a ulwürde ich entweder leer sein, liwenn die Sammlung leer ist, oder meine lis in redundante Containerelemente verpackt.

Beispiel bei diesem plnkr .

Beachten Sie den Konsolenfehler:

EXCEPTION: TypeError: Cannot read property 'name' of null in [{{thing.name}} in ShellComponent@5:12]

Mache ich etwas falsch oder ist das ein Fehler?

garethdn
quelle
stackoverflow.com/questions/40529537/… ich würde mit ng-container gehen
robert king
1
Mögliches Duplikat der Angular gefilterten Tabelle
Cobus Kruger

Antworten:

680

Angular v2 unterstützt nicht mehr als eine Strukturanweisung für dasselbe Element.
Verwenden <ng-container>Sie als Problemumgehung das Element, mit dem Sie für jede Strukturanweisung separate Elemente verwenden können, das jedoch nicht in das DOM gestempelt ist .

<ng-container *ngIf="show">
  <div *ngFor="let thing of stuff">
    {{log(thing)}}
    <span>{{thing.name}}</span>
  </div>
</ng-container>

<ng-template>( <template>vor Angular v4) ermöglicht es, dasselbe zu tun, jedoch mit einer anderen Syntax, die verwirrend ist und nicht mehr empfohlen wird

<ng-template [ngIf]="show">
  <div *ngFor="let thing of stuff">
    {{log(thing)}}
    <span>{{thing.name}}</span>
  </div>
</ng-template>
Günter Zöchbauer
quelle
5
Vielen Dank. Überraschenderweise ist immer noch undokumentiert: github.com/angular/angular.io/issues/2303
Alex Fuentes
7
Wie wird Code aussehen, wenn wir * ngIf in * ngFor haben müssen? Das heißt, die IF-Bedingung basiert auf dem Wert eines Schleifenelements.
Yuvraj Patil
22
Einfach ngForauf das <ng-container>Element und das ngIfauf das setzen <div>. Sie können auch zwei verschachtelte <ng-container>Wraps haben <div>. <ng-container>ist nur ein Hilfselement, das dem DOM nicht hinzugefügt wird.
Günter Zöchbauer
3
Ich würde vorschlagen, zu verwenden <ng-container>. Es verhält sich genauso <template>, erlaubt jedoch die Verwendung der "normalen" Syntax für strukturelle Anweisungen.
Günter Zöchbauer
2
In der Dokumentation heißt es : "Eine Strukturanweisung pro Hostelement": "Für diesen Anwendungsfall gibt es eine einfache Lösung: Setzen Sie die * ngIf auf ein Containerelement, das das * ngFor-Element umschließt." - nur wiederholen
Heringer
71

Wie alle betonten, ist es in Angular 2 nicht zulässig, mehrere Template-Direktiven in einem einzelnen Element in Angular 1.x zu haben. Weitere Informationen finden Sie hier: https://github.com/angular/angular/issues/ 7315

2016 Winkel 2 Beta

Die Lösung besteht darin, das <template>als Platzhalter zu verwenden, damit der Code so aussieht

<template *ngFor="let nav_link of defaultLinks"  >
   <li *ngIf="nav_link.visible">
       .....
   </li>
</template>

aber aus irgendeinem Grund funktioniert oben nicht 2.0.0-rc.4in diesem Fall können Sie dies verwenden

<template ngFor let-nav_link [ngForOf]="defaultLinks" >
   <li *ngIf="nav_link.visible">
       .....
   </li> 
</template>

Aktualisierte Antwort 2018

Mit Updates empfehlen wir ab sofort im Jahr 2018 die Verwendung <ng-container>von Angular V6 anstelle von<template>

Hier ist die aktualisierte Antwort.

<ng-container *ngFor="let nav_link of defaultLinks" >
   <li *ngIf="nav_link.visible">
       .....
   </li> 
</ng-container>
imal hasaranga perera
quelle
29

Wie @Zyzle und @ Günter in einem Kommentar ( https://github.com/angular/angular/issues/7315 ) erwähnt haben, wird dies nicht unterstützt.

Mit

<ul *ngIf="show">
  <li *ngFor="let thing of stuff">
    {{log(thing)}}
    <span>{{thing.name}}</span>
  </li>
</ul>

Es gibt keine leeren <li>Elemente, wenn die Liste leer ist. Auch das <ul>Element existiert nicht (wie erwartet).

Wenn die Liste ausgefüllt ist, gibt es keine redundanten Containerelemente.

Die Github-Diskussion (4792) , die @Zyzle in seinem Kommentar erwähnt hat, präsentiert auch eine andere Lösung mit <template>(unten verwende ich Ihr ursprüngliches Markup - mit <div>s):

<template [ngIf]="show">
  <div *ngFor="let thing of stuff">
    {{log(thing)}}
    <span>{{thing.name}}</span>
  </div>
</template>

Diese Lösung führt auch keine zusätzlichen / redundanten Containerelemente ein.

Mark Rajcok
quelle
1
Ich bin mir nicht sicher, warum dies nicht die akzeptierte Antwort ist. <template>Auf diese Weise können Sie ein übergeordnetes Element hinzufügen, das in der Ausgabe nicht angezeigt wird.
Evan Plaice
8

in html:

<div [ngClass]="{'disabled-field': !show}" *ngFor="let thing of stuff">
    {{thing.name}}
</div>

in css:

.disabled-field {
    pointer-events: none;
    display: none;
}
Mojtaba Ramezani
quelle
5

Sie können nicht ngForund ngIfauf dem gleichen Element haben. Sie können das Auffüllen des Arrays, in dem Sie sich befinden, so lange aufhalten, ngForbis Sie auf den Schalter in Ihrem Beispiel klicken.

Hier ist eine grundlegende (nicht großartige) Möglichkeit, dies zu tun: http://plnkr.co/edit/Pylx5HSWIZ7ahoC7wT6P

Zyzle
quelle
Warum kann er nicht beides haben? Bitte
ausarbeiten
1
Es gibt eine Diskussion darüber hier github.com/angular/angular/issues/4792
Zyzle
1
Ich weiß, warum das passiert, es ist nur, um die Qualität der Antwort zu verbessern. Es ist einfach you can'tnicht wirklich eine gute Antwort, stimmst du nicht zu?
Maurycy
Sicher, sie sollten nicht zusammen verwendet werden, nur weil die Zuordnung in eine bestimmte Reihenfolge zur Vorlage nicht garantiert, dass sie in derselben Reihenfolge ausgeführt werden. Dies erklärt jedoch nicht, was genau passiert, wenn 'Name' von 'Name' von null 'nicht gelesen werden kann.
Estus Flask
Sowohl * ngFor als auch * ngIf (mit Sternchen) sind strukturelle Anweisungen und generieren das <template> -Tag. Strukturelle Direktiven wie ngIf zaubern mithilfe des HTML 5-Vorlagen-Tags.
Pardeep Jain
5

Sie können Structural Directivein Angular nicht mehr als eines für dasselbe Element verwenden. ng-containerDies führt zu einer schlechten Verwirrung und Struktur. Sie müssen sie daher in zwei separaten verschachtelten Elementen anwenden (oder Sie können sie verwenden ). Lesen Sie diese Anweisung des Angular-Teams:

Eine Strukturrichtlinie pro Hostelement

Eines Tages möchten Sie einen HTML-Block wiederholen, aber nur, wenn eine bestimmte Bedingung erfüllt ist. Sie werden versuchen, sowohl ein * ngFor als auch ein * ngIf auf dasselbe Hostelement zu setzen . Angular lässt dich nicht. Sie können nur eine strukturelle Direktive auf ein Element anwenden.

Der Grund ist die Einfachheit. Strukturanweisungen können komplexe Dinge mit dem Host-Element und seinen Nachkommen tun. Wenn zwei Direktiven Anspruch auf dasselbe Hostelement erheben, welches hat Vorrang? Welches sollte zuerst gehen, das NgIf oder das NgFor? Kann das NgIf den Effekt des NgFor aufheben? Wenn ja (und es scheint so), wie sollte Angular die Fähigkeit verallgemeinern, andere strukturelle Richtlinien zu stornieren?

Es gibt keine einfachen Antworten auf diese Fragen. Das Verbot mehrerer struktureller Richtlinien macht sie strittig. Für diesen Anwendungsfall gibt es eine einfache Lösung: Setzen Sie das * ngIf auf ein Containerelement, das das * ngFor- Element umschließt . Eines oder beide Elemente können ein ng-Container sein, sodass Sie keine zusätzlichen HTML-Ebenen einführen müssen.

Sie können also ng-container(Angular4) als Wrapper (wird aus dem Dom gelöscht) oder als Div oder Span verwenden, wenn Sie eine Klasse oder andere Attribute wie folgt haben:

<div class="right" *ngIf="show">
  <div *ngFor="let thing of stuff">
    {{log(thing)}}
    <span>{{thing.name}}</span>
  </div>
</div>
Alireza
quelle
4

Dies funktioniert, aber das Element befindet sich weiterhin im DOM.

.hidden {
    display: none;
}

<div [class.hidden]="!show" *ngFor="let thing of stuff">
    {{log(thing)}}
    <span>{{thing.name}}</span>
</div>
ronald8192
quelle
Dies ist ein sehr einfacher Hack für die <select> <option> -Kombination, bei der ich einfach gefilterte Elemente anstelle der vollständigen Liste
anzeigen
Vielen Dank!
Abhishek Sharma
3

In der folgenden Tabelle sind nur Elemente aufgeführt, für die ein "Anfänger" -Wert festgelegt wurde. Erfordert sowohl das *ngForals auch das *ngIf, um unerwünschte Zeilen in HTML zu verhindern.

Ursprünglich hatte *ngIfund *ngForauf dem gleichen <tr>Tag, funktioniert aber nicht. Ein <div>for für die *ngForSchleife hinzugefügt und *ngIfin das <tr>Tag eingefügt, funktioniert wie erwartet.

<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>
charliebear240
quelle
1
Ich denke nicht, dass ein <div>Tisch in einem Tisch eine gute Idee ist, besonders wenn es bessere Alternativen gibt. Haben Sie überprüft, ob dies in IE funktioniert, was besonders wählerisch in Bezug auf Elemente in<table>
Günter Zöchbauer
3

Auf Angular2 Beta 8 aktualisiert

Ab Angular2 Beta 8 können wir nun dieselbe Komponente verwenden *ngIfund hier sehen .*ngFor

Wechseln:

Manchmal können wir keine HTML-Tags in anderen wie in tr, th( table) oder in li( ul) verwenden. Wir können kein anderes HTML-Tag verwenden, müssen jedoch in derselben Situation eine Aktion ausführen, damit wir das HTML5-Feature-Tag <template>auf diese Weise ausführen können .

ngFür die Verwendung der Vorlage:

<template ngFor #abc [ngForOf]="someArray">
    code here....
</template>

ngWenn Sie eine Vorlage verwenden:

<template [ngIf]="show">
    code here....
</template>    

Weitere Informationen zu Strukturrichtlinien in Angular2 finden Sie hier .

Pardeep Jain
quelle
1
<div *ngFor="let thing of show ? stuff : []">
  {{log(thing)}}
  <span>{{thing.name}}</span>
</div>
Prashant Borde
quelle
1

Sie können auch ng-template(anstelle der Vorlage. Siehe den Hinweis zur Einschränkung der Verwendung des Vorlagen-Tags) zum Anwenden von * ngFor und ngIf auf dasselbe HTML-Element verwenden. Hier ist ein Beispiel, in dem Sie sowohl * ngIf als auch * ngFor für dasselbe tr- Element in der Winkeltabelle verwenden können .

<tr *ngFor = "let fruit of fruiArray">
    <ng-template [ngIf] = "fruit=='apple'>
        <td> I love apples!</td>
    </ng-template>
</tr>

wo fruiArray = ['apple', 'banana', 'mango', 'pineapple'].

Hinweis:

Die Einschränkung, nur das templateTag anstelle des ng-templateTags zu verwenden, besteht darin, dass es StaticInjectionErroran einigen Stellen ausgelöst wird .

Steffi Keran Rani J.
quelle
0

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

Rajiv
quelle
li Elemente werden nur angezeigt, wenn sie einen Namen haben.
Rajiv
3
Wie schafft diese Antwort hier einen Mehrwert? Es gibt nichts, was nicht bereits von den anderen Antworten bereitgestellt wurde, oder habe ich etwas verpasst?
Günter Zöchbauer
0

Sie können nicht mehrere strukturelle Anweisungen für dasselbe Element verwenden. Wickeln Sie Ihr Element ein ng-templateund verwenden Sie dort eine strukturelle Richtlinie

Pradip Patil
quelle
0

Sie können dies auch auf andere Weise tun, indem Sie die Array-Länge überprüfen

<div *ngIf="stuff.length>0">
  <div *ngFor="let thing of stuff">
    {{log(thing)}}
    <span>{{thing.name}}</span>
  </div>
</div>
SMS
quelle
-1

app.component.ts

import {NgModule, Component} from '@angular/core';
import {BrowserModule} from '@angular/platform-browser';
import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';

@Component({
  selector: 'ngif-example',
  template: `
<h4>NgIf</h4>
<ul *ngFor="let person of people">
  <li *ngIf="person.age < 30"> (1)
  {{ person.name }} ({{ person.age }})
  </li>
</ul>
`
})
class NgIfExampleComponent {

  people: any[] = [
    {
      "name": "yogesh ",
      "age": 35
    },
    {
      "name": "gamesh",
      "age": 32
    },
    {
      "name": " Meyers",
      "age": 21
    },
    {
      "name": " Ellis",
      "age": 34
    },
    {
      "name": " Tyson",
      "age": 32
    }
  ];
}

app.component.html

<ul *ngFor="let person of people" *ngIf="person.age < 30">
  <li>{{ person.name }}</li>
</ul>

Ausgabe

Meyers(21)
Yogesh Waghmare
quelle