* ngIfと* ngForが同じ要素でエラーを引き起こす


473

私は角度のを使用しようとすると問題を抱えている*ngForし、*ngIf同じ要素に。

のコレクションをループしようとする*ngForと、コレクションはと見nullなされ、その結果、テンプレートのプロパティにアクセスしようとすると失敗します。

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

}

私は簡単な解決策が移動することです知っている*ngIfレベルをアップしますが、リストの項目をループのようなシナリオのためにul、私は空のいずれかで終わるだろうliコレクションが空である、または私の場合はlisが冗長コンテナ要素に包まれました。

このplnkrでの例。

コンソールエラーに注意してください。

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

私は何か間違ったことをしていますか、これはバグですか?


stackoverflow.com/questions/40529537/... 私はNG-コンテナと一緒に行きたい
ロバート・キング

回答:


680

Angular v2は、同じ要素で複数の構造ディレクティブをサポートしていません。
回避策として、<ng-container>構造ディレクティブごとに個別の要素を使用できるようにする要素を使用しますが、DOMにスタンプされません

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

<ng-template><template>Angular v4より前)同じことを行うことができますが、構文が異なり、混乱を招き、推奨されなくなりました。

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

5
どうもありがとう。驚くべきことに、まだ文書化されていません
Alex Fuentes

7
* ngFor内に* ngIfが必要な場合、コードはどのように見えますか?つまり、IF条件はループ要素の値に基づきます。
Yuvraj Patil 2017年

22
ただ置くngFor<ng-container>要素とngIf<div>。2つのネストされたを<ng-container>ラップすることもでき<div>ます。<ng-container>DOMに追加されない単なるヘルパー要素です。
ギュンターZöchbauer

3
の使用をお勧めし<ng-container>ます。動作は同じです<template>が、構造ディレクティブに「通常の」構文を使用できます。
ギュンターZöchbauer

2
ドキュメントによると「ホスト要素ごとに1つの構造ディレクティブ」:「このユースケースには簡単な解決策があります。* ngFor要素をラップするコンテナ要素に* ngIfを置きます。」-繰り返します
ヘリンガー

71

単一の要素に複数のテンプレートディレクティブがあることは誰でも指摘したように、angular 1.xでは機能しますが、Angular 2では許可されていません。詳細については、https//github.com/angular/angular/issues/を参照してください。 7315

2016 Angular 2ベータ

解決策はを<template>プレースホルダーとして使用することなので、コードは次のようになります

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

しかし、上記の理由で上記のいくつかの理由で機能しない2.0.0-rc.4場合は、これを使用できます

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

2018年の回答の更新

更新により、現時点では2018年のAngular v6 <ng-container>ではなく、<template>

これが更新された答えです。

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

29

@Zyzleが言及し、コメントで@Günterが言及したように(https://github.com/angular/angular/issues/7315)、これはサポートされていません。

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

<li>リストが空の場合、空の要素はありません。<ul>要素さえ存在しない(期待どおり)。

リストが入力されると、冗長なコンテナ要素はありません。

githubの議論(4792) @Zyzleは彼のコメントに言及したことも、使用して別の解決策を提示<template>- (使用して私はあなたの元のマークアップを使用してい下回っ<div>:S)

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

このソリューションでは、追加/冗長コンテナ要素も導入されていません。


1
なぜこれが受け入れられない答えなのか、私にはわかりません。<template>出力に表示されない親要素を追加する方法です。
Evan Plaice、2016年

8

HTML:

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

CSS:

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

5

あなたは持つことができないngForngIf同じ要素に。あなたができることはngFor、あなたの例のトグルがクリックされるまで、あなたが使用している配列への入力を保留することです。

これがあなたがそれを行うことができる基本的な(素晴らしいではない)方法です:http//plnkr.co/edit/Pylx5HSWIZ7ahoC7wT6P


なぜ彼は両方を持つことができないのですか?精巧にお願いします
モーリシー2016年

1
その周りの議論がここにありますgithub.com/angular/angular/issues/4792
Zyzle

1
私はそれがなぜ起こっているのか知っています、それは単に答えの質を改善することです、明白に言うことyou can'tは本当に良い答えではありません、あなたは同意しませんか?
maurycy

もちろん、テンプレートに特定の順序で配置しても、同じ順序で実行されるとは限らないため、これらを一緒に使用しないでください。しかし、これは「ヌルのプロパティ 'name'を読み取れません」がスローされたときに正確に何が起こるかを説明していません。
Estus Flask

* ngForと* ngIf(アスタリスク付き)は両方とも構造ディレクティブであり、<template>タグを生成します。ngIfのような構造ディレクティブは、HTML 5テンプレートタグを使用することで魔法をかけます。
Pardeep Jain

5

Structural Directive同じ要素で複数のAngularを使用することはできません。混乱と構造に悪影響を与えるため、2つの別々のネストされた要素にそれらを適用する必要があります(またはを使用できますng-container)。Angularチームから次のステートメントをお読みください。

ホスト要素ごとに1つの構造ディレクティブ

いつか、特定の条件がtrueの場合にのみ、HTMLのブロックを繰り返したいと思うでしょう。あなたは、両方入れてみます* ngFor* ngIfを同じホスト要素に。Angularはあなたを許しません。1つの要素に適用できる構造ディレクティブは1つだけです。

その理由は単純さです。構造ディレクティブは、ホスト要素とその子孫で複雑なことを実行できます。2つのディレクティブが同じホスト要素を要求する場合、どちらが優先されますか?NgIfとNgForのどちらを先にすべきですか?NgIfはNgForの効果をキャンセルできますか?そうである場合(そうである必要があるように思われる場合)、Angularは他の構造的ディレクティブをキャンセルする機能をどのように一般化する必要がありますか?

これらの質問に対する簡単な答えはありません。複数の構造的指令を禁止すると、それらは悲観的になります。このユースケースのための簡単な解決策があります:置く* ngIfを包むコンテナ要素に * ngFor要素を。1つまたは両方の要素をng-containerにすることができるため、追加のレベルのHTMLを導入する必要はありません。

したがってng-container、次のようなクラスまたは他の属性がある場合は、(Angular4)をラッパー(domから削除されます)またはdivまたはspanとして使用できます。

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

4

これは機能しますが、要素はDOMに残ります。

.hidden {
    display: none;
}

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

これは、<select>と<option>の組み合わせの非常に簡単なハックです。完全なリストではなく、フィルターされたアイテムを表示したいだけです
davyzhang

どうもありがとうございました!
Abhishek Sharma

3

以下の表は、「初心者」の値が設定されている項目のみを示しています。*ngForとの両方*ngIfがHTMLの不要な行を防止する必要があります。

もともと持っていた*ngIf*ngFor同じで<tr>タグが、動作しません。<div>for *ngForループを追加*ngIfして<tr>タグに配置すると、期待どおりに動作します。

<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
<div>テーブルの内側は、特により良い代替案がある場合には、良い考えではないと思います。これで要素については特に選り好みしているIEで動作するかどうか、あなたが確認しました<table>
ギュンターZöchbauer

3

angular2ベータ8に更新されました

今、私たちが使用することができますangular2ベータ8からと*ngIfし、*ngFor同じ構成要素にこちらをご覧ください

代わりの:

時々 、私たちはのように別の内側にHTMLタグを使用することはできませんtrthtable)またはでliul)。別のHTMLタグを使用することはできませんが<template>、この方法でHTML5機能タグを使用できるように、同じ状況で何らかのアクションを実行する必要があります。

テンプレートを使用するngFor:

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

テンプレートを使用するngIf:

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

angular2の構造ディレクティブの詳細については、こちらを参照してください


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

1

同じHTML要素に* ngForngIfのng-template両方を適用するために(テンプレートの代わりに。テンプレートタグを使用する場合の注意については、注意を参照)を使用することもできます。これは、角度テーブルの同じtr要素に* ngIfと* ngForの両方を使用できる例です。

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

どこfruiArray = ['apple', 'banana', 'mango', 'pineapple']

注意:

templateタグの代わりにng-templateタグだけを使用する場合の注意点はStaticInjectionError、いくつかの場所でスローされることです。


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>


liアイテムは、名前がある場合にのみ表示されます。
Rajiv

3
この回答はどのようにしてここで付加価値をもたらしますか?他の回答で提供されていないものは何も提供されていませんか、何かを見逃していませんか?
ギュンターZöchbauer

0

同じ要素で複数の構造ディレクティブを使用することはできません。要素をラップし、ng-templateそこで1つの構造ディレクティブを使用します


0

配列の長さをチェックすることで、これを別の方法で行うことができます

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

出力

Meyers(21)
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.