* ngIf dan * ngFor pada elemen yang sama menyebabkan kesalahan

473

Saya mengalami masalah dengan mencoba menggunakan Angular *ngFordan *ngIfpada elemen yang sama.

Saat mencoba untuk mengulang koleksi di *ngFor, koleksi dilihat sebagai nulldan akibatnya gagal ketika mencoba mengakses propertinya di templat.

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

}

Saya tahu solusi mudahnya adalah *ngIfnaik level tetapi untuk skenario seperti pengulangan item daftar ul, saya akan berakhir dengan kosong lijika koleksinya kosong, atau saya lidibungkus dengan elemen kontainer yang berlebihan.

Contoh di plnkr ini .

Perhatikan kesalahan konsol:

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

Apakah saya melakukan sesuatu yang salah atau ini bug?

Garethdn
sumber
stackoverflow.com/questions/40529537/... Aku akan pergi dengan ng-container
robert king
1
Kemungkinan duplikat dari tabel berfilter sudut
Cobus Kruger

Jawaban:

680

V2 sudut tidak mendukung lebih dari satu arahan struktural pada elemen yang sama.
Sebagai solusinya gunakan <ng-container>elemen yang memungkinkan Anda untuk menggunakan elemen terpisah untuk setiap arahan struktural, tetapi tidak dicap ke DOM .

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

<ng-template>( <template>Sebelum Angular v4) memungkinkan untuk melakukan hal yang sama tetapi dengan sintaks berbeda yang membingungkan dan tidak lagi direkomendasikan

<ng-template [ngIf]="show">
  <div *ngFor="let thing of stuff">
    {{log(thing)}}
    <span>{{thing.name}}</span>
  </div>
</ng-template>
Günter Zöchbauer
sumber
5
Terima kasih banyak. Anehnya masih tidak berdokumen: github.com/angular/angular.io/issues/2303
Alex Fuentes
7
Bagaimana kode akan terlihat ketika kita harus memiliki * ngIf di dalam * ngFor? Yaitu kondisi JIKA akan didasarkan pada nilai elemen loop.
Yuvraj Patil
22
Cukup letakkan ngFordi <ng-container>elemen dan ngIfdi <div>. Anda juga dapat memiliki dua sarang yang <ng-container>dibungkus <div>. <ng-container>hanyalah elemen pembantu yang tidak akan ditambahkan ke DOM.
Günter Zöchbauer
3
Saya sarankan menggunakan <ng-container>. Berperilaku sama seperti <template>tetapi memungkinkan untuk menggunakan sintaks "normal" untuk arahan struktural.
Günter Zöchbauer
2
Dokumentasi mengatakan : "Satu arahan struktural per elemen host": "Ada solusi mudah untuk kasus penggunaan ini: letakkan * ngIf pada elemen kontainer yang membungkus elemen * ngFor." - Hanya mengulangi
heringer
71

Seperti yang semua orang tunjukkan meskipun memiliki beberapa arahan template dalam satu elemen berfungsi dalam angular 1.x itu tidak diizinkan dalam Angular 2. Anda dapat menemukan lebih banyak info dari sini: https://github.com/angular/angular/issues/ 7315

2016 sudut 2 beta

solusinya adalah dengan menggunakan <template>sebagai pengganti, jadi kodenya seperti ini

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

tetapi untuk beberapa alasan di atas tidak berfungsi 2.0.0-rc.4jika Anda dapat menggunakan ini

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

Jawaban Diperbarui 2018

Dengan pembaruan, sekarang di 2018 angular v6 merekomendasikan untuk menggunakan, <ng-container>bukan<template>

jadi inilah jawaban yang diperbarui.

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

Seperti @Zyzle sebutkan, dan @ Günter disebutkan dalam komentar ( https://github.com/angular/angular/issues/7315 ), ini tidak didukung.

Dengan

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

tidak ada <li>elemen kosong ketika daftar kosong. Bahkan <ul>elemen tidak ada (seperti yang diharapkan).

Ketika daftar diisi, tidak ada elemen wadah yang berlebihan.

The github diskusi (4792) yang @Zyzle disebutkan dalam komentarnya juga menyajikan solusi lain menggunakan <template>(di bawah Saya menggunakan markup asli Anda - menggunakan <div>s):

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

Solusi ini juga tidak memperkenalkan elemen wadah ekstra / berlebihan.

Mark Rajcok
sumber
1
Saya tidak yakin mengapa ini bukan jawaban yang diterima. <template>adalah cara untuk menambahkan elemen induk yang tidak akan muncul di output.
Evan Plaice
8

dalam html:

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

dalam css:

.disabled-field {
    pointer-events: none;
    display: none;
}
mojtaba ramezani
sumber
5

Anda tidak dapat memiliki ngFordan ngIfpada elemen yang sama. Apa yang bisa Anda lakukan adalah menunda mengisi array yang Anda gunakan ngForsampai sakelar pada contoh Anda diklik.

Inilah cara dasar (tidak hebat) yang bisa Anda lakukan: http://plnkr.co/edit/Pylx5HSWIZ7ahoC7wT6P

Zyzle
sumber
Kenapa dia tidak bisa memiliki keduanya? Tolong rumit
maurycy
1
Ada diskusi tentang itu di sini github.com/angular/angular/issues/4792
Zyzle
1
Saya tahu mengapa itu terjadi, hanya untuk meningkatkan kualitas jawaban, jelas mengatakan you can'tbukan jawaban yang baik, tidakkah Anda setuju?
maurycy
Tentu, mereka tidak boleh digunakan bersama hanya karena menempatkan mereka dalam urutan tertentu ke template tidak menjamin bahwa mereka akan dieksekusi dalam urutan yang sama. Tetapi ini tidak menjelaskan apa yang sebenarnya terjadi ketika 'Tidak dapat membaca properti' nama 'dari nol' dilemparkan.
Estus Flask
Baik * ngFor dan * ngIf (dengan tanda bintang) adalah arahan struktural dan keduanya menghasilkan tag <template>. Arahan struktural, seperti ngIf, melakukan keajaiban mereka dengan menggunakan tag template HTML 5.
Pardeep Jain
5

Anda tidak dapat menggunakan lebih dari satu Structural Directivedi Angular pada elemen yang sama, itu membuat kebingungan dan struktur yang buruk, jadi Anda perlu menerapkannya dalam 2 elemen bersarang terpisah (atau Anda dapat menggunakan ng-container), baca pernyataan ini dari tim Angular:

Satu arahan struktural per elemen host

Suatu hari Anda akan ingin mengulangi blok HTML tetapi hanya ketika kondisi tertentu benar. Anda akan mencoba meletakkan * ngFor dan * ngIf pada elemen host yang sama. Sudut tidak akan membiarkanmu. Anda dapat menerapkan hanya satu arahan struktural untuk suatu elemen.

Alasannya adalah kesederhanaan. Arahan struktural dapat melakukan hal-hal kompleks dengan elemen host dan turunannya. Ketika dua arahan mengklaim elemen host yang sama, mana yang diutamakan? Yang harus pergi dulu, NgIf atau NgFor? Bisakah NgIf membatalkan efek NgFor? Jika demikian (dan sepertinya memang demikian), bagaimana seharusnya Angular menggeneralisasi kemampuan untuk membatalkan arahan struktural lainnya?

Tidak ada jawaban mudah untuk pertanyaan-pertanyaan ini. Melarang banyak arahan struktural membuat mereka diperdebatkan. Ada solusi mudah untuk kasus penggunaan ini: letakkan * ngIf pada elemen kontainer yang membungkus elemen * ngFor . Satu atau kedua elemen bisa menjadi wadah sehingga Anda tidak perlu memperkenalkan level HTML tambahan.

Jadi Anda dapat menggunakan ng-container(Angular4) sebagai pembungkus (akan dihapus dari dom) atau div atau rentang jika Anda memiliki kelas atau atribut lainnya seperti di bawah ini:

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

Ini akan berfungsi tetapi elemennya masih di DOM.

.hidden {
    display: none;
}

<div [class.hidden]="!show" *ngFor="let thing of stuff">
    {{log(thing)}}
    <span>{{thing.name}}</span>
</div>
ronald8192
sumber
Ini adalah
retasan yang
Terima kasih banyak!
Abhishek Sharma
3

Tabel di bawah ini hanya mencantumkan item yang memiliki set nilai "pemula". Membutuhkan keduanya *ngFordan *ngIfuntuk mencegah baris yang tidak diinginkan dalam html.

Awalnya memiliki *ngIfdan *ngForpada <tr>tag yang sama , tetapi tidak berfungsi. Menambahkan <div>untuk *ngForloop dan ditempatkan *ngIfdi <tr>tag, berfungsi seperti yang diharapkan.

<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
sumber
1
Saya tidak berpikir <div>di dalam meja adalah ide goos, terutama ketika ada alternatif yang lebih baik. Sudahkah Anda memeriksa apakah dengan demikian bekerja di IE yang terutama pilih-pilih elemen di<table>
Günter Zöchbauer
3

Diperbarui ke angular2 beta 8

Sekarang mulai dari angular2 beta 8 kita bisa menggunakan *ngIfdan *ngForpada komponen yang sama lihat di sini .

Bergantian:

Terkadang kami tidak dapat menggunakan tag HTML di dalam yang lain seperti di tr, th( table) atau di li( ul). Kami tidak dapat menggunakan tag HTML lain tetapi kami harus melakukan beberapa tindakan dalam situasi yang sama sehingga kami dapat menggunakan tag fitur HTML5 <template>dengan cara ini.

ngFor menggunakan templat:

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

ngIf menggunakan templat:

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

Untuk informasi lebih lanjut tentang arahan struktural di angular2 lihat di sini .

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

Anda juga dapat menggunakan ng-template(alih-alih templat. Lihat catatan untuk peringatan menggunakan tag templat) untuk menerapkan * ngFor dan ngIf pada elemen HTML yang sama. Berikut adalah contoh di mana Anda dapat menggunakan * ngIf dan * ngFor untuk elemen tr yang sama di tabel sudut.

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

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

catatan:

Peringatan menggunakan templatetag bukan ng-templatetag adalah bahwa ia melempar StaticInjectionErrordi beberapa tempat.

Steffi Keran Rani J
sumber
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
sumber
item li hanya ditampilkan jika memiliki nama.
Rajiv
3
Bagaimana jawaban ini menambah nilai di sini? Itu tidak memberikan apa pun yang tidak disediakan oleh jawaban lain atau apakah saya melewatkan sesuatu?
Günter Zöchbauer
0

Anda tidak dapat menggunakan banyak arahan struktural pada elemen yang sama. Bungkus elemen Anda ng-templatedan gunakan satu arahan struktural di sana

Pradip Patil
sumber
0

Anda dapat melakukan ini dengan cara lain dengan memeriksa panjang array

<div *ngIf="stuff.length>0">
  <div *ngFor="let thing of stuff">
    {{log(thing)}}
    <span>{{thing.name}}</span>
  </div>
</div>
SMS
sumber
-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>

Keluaran

Meyers(21)
yogesh waghmare
sumber