* ng If và * ngFor trên cùng một phần tử gây ra lỗi


473

Tôi đang gặp vấn đề với việc cố gắng sử dụng Angular *ngFor*ngIftrên cùng một yếu tố.

Khi cố gắng lặp qua bộ sưu tập trong *ngFor, bộ sưu tập được xem là nullvà do đó không thành công khi cố gắng truy cập các thuộc tính của nó trong mẫu.

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

}

Tôi biết giải pháp dễ dàng là *ngIftăng cấp độ nhưng đối với các tình huống như lặp qua các mục trong danh sách ul, tôi sẽ kết thúc bằng một khoảng trống linếu bộ sưu tập trống hoặc ligói của tôi được bọc trong các phần tử container dự phòng.

Ví dụ tại plnkr này .

Lưu ý lỗi giao diện điều khiển:

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

Tôi đang làm gì đó sai hay đây là một lỗi?



1
Bản sao có thể có của bảng đã lọc Angular
Cobus Kruger

Câu trả lời:


680

Angular v2 không hỗ trợ nhiều hơn một chỉ thị cấu trúc trên cùng một phần tử.
Như một cách giải quyết, sử dụng <ng-container>phần tử cho phép bạn sử dụng các phần tử riêng biệt cho mỗi chỉ thị cấu trúc, nhưng nó không được đóng dấu vào DOM .

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

<ng-template>( <template>trước Angular v4) cho phép thực hiện tương tự nhưng với một cú pháp khác gây nhầm lẫn và không còn được khuyến nghị

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

5
Cảm ơn rất nhiều. Đáng ngạc nhiên là vẫn không có giấy tờ: github.com/angular/angular.io/issues/2303
Alex Fuentes

7
Mã sẽ trông như thế nào khi chúng ta phải có * ngNếu bên trong * ngFor? Điều kiện IF sẽ dựa trên giá trị của một phần tử vòng lặp.
Yuvraj Patil

22
Chỉ cần đặt ngFortại các <ng-container>yếu tố và ngIftại <div>. Bạn cũng có thể có hai <ng-container>gói lồng nhau <div>. <ng-container>chỉ là một yếu tố trợ giúp sẽ không được thêm vào DOM.
Günter Zöchbauer

3
Tôi muốn đề nghị sử dụng <ng-container>. Nó hoạt động giống như <template>nhưng cho phép sử dụng cú pháp "bình thường" cho các chỉ thị cấu trúc.
Günter Zöchbauer

2
Tài liệu nói : "Một chỉ thị cấu trúc cho mỗi phần tử máy chủ": "Có một giải pháp dễ dàng cho trường hợp sử dụng này: đặt * ng If trên phần tử container bao bọc phần tử * ngFor." - chỉ cần nhắc lại
heringer

71

Như mọi người đã chỉ ra mặc dù có nhiều chỉ thị mẫu trong một yếu tố duy nhất hoạt động ở góc 1.x, nó không được phép trong Angular 2. bạn có thể tìm thêm thông tin từ đây: https://github.com/angular/angular/issues/ 7315

2016 góc cạnh 2 beta

Giải pháp là sử dụng <template>như một trình giữ chỗ, vì vậy mã đi như thế này

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

nhưng vì một số lý do ở trên không hoạt động trong 2.0.0-rc.4trường hợp đó bạn có thể sử dụng cái này

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

Cập nhật câu trả lời 2018

Với các bản cập nhật, ngay bây giờ trong năm 2018 góc v6 khuyên bạn nên sử dụng <ng-container>thay vì<template>

Vì vậy, đây là câu trả lời cập nhật.

<ng-container *ngFor="let nav_link of defaultLinks" >
   <li *ngIf="nav_link.visible">
       .....
   </li> 
</ng-container>

29

Như @Zyzle đã đề cập và @ Günter đã đề cập trong một nhận xét ( https://github.com/angular/angular/issues/7315 ), điều này không được hỗ trợ.

Với

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

không có <li>phần tử trống khi danh sách trống. Ngay cả các <ul>yếu tố không tồn tại (như mong đợi).

Khi danh sách được điền, không có phần tử container dự phòng.

Cuộc thảo luận về github (4792) mà @Zyzle đã đề cập trong bình luận của anh ấy cũng trình bày một giải pháp khác bằng cách sử dụng <template>(bên dưới tôi đang sử dụng đánh dấu ban đầu của bạn - sử dụng <div>s):

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

Giải pháp này cũng không giới thiệu bất kỳ yếu tố container thừa / dự phòng.


1
Tôi không chắc tại sao đây không phải là câu trả lời được chấp nhận. <template>là cách để thêm một phần tử cha sẽ không hiển thị trong đầu ra.
Evan Plaice

8

trong html:

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

trong css:

.disabled-field {
    pointer-events: none;
    display: none;
}

5

Bạn không thể có ngForngIftrên cùng một yếu tố. Những gì bạn có thể làm là giữ lại việc điền vào mảng bạn đang sử dụng ngForcho đến khi bật nút trong ví dụ của bạn.

Đây là một cách cơ bản (không tuyệt vời) mà bạn có thể làm: http://plnkr.co/edit/Pylx5HSWIZ7ahoC7wT6P


Tại sao anh ta không thể có cả hai? Vui lòng xây dựng
maurycy

1
Có một cuộc thảo luận xung quanh đó tại đây github.com/angular/angular/issues/4792
Zyzle 7/1/2016

1
Tôi biết tại sao điều đó xảy ra, đó chỉ là để cải thiện chất lượng câu trả lời, nói rõ ràng you can'tkhông thực sự là một câu trả lời hay, bạn có đồng ý không?
maurycy

Chắc chắn, chúng không nên được sử dụng cùng nhau chỉ vì đặt chúng theo thứ tự nhất định để mẫu không đảm bảo rằng chúng sẽ được thực hiện theo cùng một thứ tự. Nhưng điều này không giải thích chính xác điều gì xảy ra khi 'Không thể đọc tên' thuộc tính 'của null'.
Bình Estus

Cả * ngFor và * ng If (có dấu hoa thị) đều là các chỉ thị cấu trúc và chúng tạo ra thẻ <template>. Các chỉ thị cấu trúc, như ngNếu, thực hiện phép thuật của họ bằng cách sử dụng thẻ mẫu HTML 5.
Pardeep Jain

5

Bạn không thể sử dụng nhiều hơn một Structural Directivetrong Angular trên cùng một phần tử, điều này gây nhầm lẫn và cấu trúc xấu, vì vậy bạn cần áp dụng chúng trong 2 phần tử lồng nhau riêng biệt (hoặc bạn có thể sử dụng ng-container), đọc câu này từ nhóm Angular:

Một chỉ thị cấu trúc cho mỗi phần tử máy chủ

Một ngày nào đó bạn sẽ muốn lặp lại một khối HTML nhưng chỉ khi một điều kiện cụ thể là đúng. Bạn sẽ cố gắng đặt cả * ngFor* ngNếu trên cùng một phần tử máy chủ. Angular sẽ không cho phép bạn. Bạn chỉ có thể áp dụng một chỉ thị cấu trúc cho một phần tử.

Lý do là sự đơn giản. Chỉ thị cấu trúc có thể làm những điều phức tạp với yếu tố chủ và con cháu của nó. Khi hai chỉ thị đặt yêu cầu cho cùng một phần tử máy chủ, cái nào được ưu tiên? Cái nào nên đi trước, Ng If hay NgFor? NgNếu có thể hủy tác dụng của NgFor không? Nếu vậy (và có vẻ như nó phải như vậy), Angular nên khái quát hóa khả năng hủy bỏ cho các chỉ thị cấu trúc khác như thế nào?

Không có câu trả lời dễ dàng cho những câu hỏi này. Cấm nhiều chỉ thị cấu trúc làm cho chúng moot. Có một giải pháp dễ dàng cho trường hợp sử dụng này: đặt * ng If trên phần tử container chứa phần tử * ngFor . Một hoặc cả hai yếu tố có thể là một ng-container để bạn không phải giới thiệu các cấp độ HTML bổ sung.

Vì vậy, bạn có thể sử dụng ng-container(Angular4) làm trình bao bọc (sẽ bị xóa khỏi dom) hoặc div hoặc span nếu bạn có lớp hoặc một số thuộc tính khác như dưới đây:

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

4

Điều này sẽ hoạt động nhưng phần tử vẫn sẽ trong DOM.

.hidden {
    display: none;
}

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

Đây là một cách hack rất dễ dàng cho sự kết hợp <select> <tùy chọn>, mà tôi chỉ đơn giản muốn hiển thị các mục được lọc thay vì danh sách đầy đủ
deefzhang 17/2/2017

Cảm ơn rât nhiều!
Shhishek Sharma

3

Bảng bên dưới chỉ liệt kê các mục có giá trị "người mới bắt đầu". Yêu cầu cả *ngFor*ngIfđể ngăn các hàng không mong muốn trong html.

Ban đầu có *ngIf*ngFortrên cùng một <tr>thẻ, nhưng không hoạt động. Thêm một <div>cho *ngForvòng lặp và được đặt *ngIftrong <tr>thẻ, các công trình như mong đợi.

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

1
Tôi không nghĩ rằng <div>bên trong một cái bàn là một ý tưởng hay, đặc biệt là khi có những lựa chọn thay thế tốt hơn. Bạn đã kiểm tra xem do đó hoạt động trong IE, đặc biệt kén chọn các yếu tố trong<table>
Günter Zöchbauer

3

Đã cập nhật lên angular2 beta 8

Bây giờ, từ angular2 beta 8, chúng ta có thể sử dụng *ngIf*ngFortrên cùng một thành phần xem tại đây .

Luân phiên:

Đôi khi, chúng ta không thể sử dụng các thẻ HTML bên trong một thẻ khác như in tr, th( table) hoặc in li( ul). Chúng tôi không thể sử dụng thẻ HTML khác nhưng chúng tôi phải thực hiện một số hành động trong cùng một tình huống để chúng tôi có thể gắn thẻ tính năng HTML5 <template>theo cách này.

ngFor sử dụng mẫu:

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

ngNếu sử dụng mẫu:

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

Để biết thêm thông tin về các chỉ thị cấu trúc trong angular2 xem tại đây .


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

1

Bạn cũng có thể sử dụng ng-template(thay vì mẫu. Xem ghi chú cho cảnh báo sử dụng thẻ mẫu) để áp dụng cả * ngForng If trên cùng một phần tử HTML. Dưới đây là một ví dụ nơi bạn có thể sử dụng cả * ng If và * ngFor cho cùng một phần tử tr trong bảng góc.

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

nơi fruiArray = ['apple', 'banana', 'mango', 'pineapple'].

Ghi chú:

Nhắc nhở của việc chỉ sử dụng templatethẻ thay vì ng-templatethẻ là nó ném StaticInjectionErrorở một số nơi.


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>


mục li chỉ được hiển thị nếu nó có tên.
Rajiv

3
Làm thế nào để câu trả lời này thêm giá trị ở đây? Nó không cung cấp bất cứ điều gì không được cung cấp bởi các câu trả lời khác hoặc tôi đã bỏ lỡ điều gì?
Günter Zöchbauer 2/11/2016

0

Bạn không thể sử dụng nhiều chỉ thị cấu trúc trên cùng một yếu tố. Gói phần tử của bạn vào ng-templatevà sử dụng một chỉ thị cấu trúc ở đó


0

Bạn có thể làm điều này theo cách khác bằng cách kiểm tra độ dài mảng

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

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

Đầu ra

Meyers(21)
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.