web-dev-qa-db-ja.com

* ngIfと* ngForが同じ要素にあるとエラーになる

同じ要素に対してAngularの*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を1レベル上げることですが、ul内のリスト項目をループするようなシナリオでは、コレクションが空の場合は空のli、冗長なコンテナ要素でラップされた場合はlisになります。

これでの例 plnkr

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

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

私は何か悪いことをしていますか、これはバグですか?

340
garethdn

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>(Angular v4の前の<template>)は同じことをすることを可能にしますが、異なる構文を使用します。

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

1つの要素に複数のテンプレートディレクティブを設定することはAngular 1.xでは機能しますが、Angular 2では許可されていません。 https:// github .com /角度/角度/ issue/7315

2016角度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>
43

もう1つ解決策があります。

[hidden]の代わりに*ngIfを使用してください

<div [hidden]="!show" *ngFor="let thing of stuff">

違いは、*ngIfはDOMから要素を削除するのに対し、[hidden]はdisplay:noneを設定することで実際にはCSSスタイルで再生されることです。

30
Ajmal sha

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

この解決策はまた、余分な/冗長なコンテナ要素を導入しない。

29
Mark Rajcok

同じ要素にngForngIfを含めることはできません。あなたの例のトグルがクリックされるまであなたがすることができるのはあなたがngForであなたが使っている配列を生成することを延期することです。

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

5
Zyzle

以下の表は、「初心者」の値が設定されている項目のみを示しています。 htmlの不要な行を防ぐために*ngFor*ngIfの両方が必要です。

もともと*ngIf*ngForは同じ<tr>タグにありましたが、動作しません。 <div>ループに*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>
3
charliebear240

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

.hidden{
    display: none;
}

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

Angular2 beta 8にアップデート

今度はangular2 beta 8以降、同じコンポーネント ここを参照*ngIf*ngForを使うことができます。

代わりの:

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

ngFor for template:

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

テンプレートを使用したngIf:

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

Angular2 ここを参照 の構造化命令の詳細については。

2
Pardeep Jain

同じ要素のAngularに複数のStructural Directiveを使用することはできません。混乱や構造が悪くなるので、2つの別々のネストした要素に適用する必要があります(またはng-containerを使用できます)。 Angularチームからの声明:

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

いつの日かHTMLのブロックを繰り返したいと思うでしょうが、それは特定の条件が当てはまるときだけです。同じHost要素に * ngFor * ngIf の両方を付けてみます。 Angularはあなたをさせません。 1つの要素に適用できる構造指令は1つだけです。

その理由は単純さです。構造指令は、Host要素とその子孫を使って複雑なことをすることができます。 2つのディレクティブが同じHost要素を主張するとき、どちらが優先されますか? NgIfとNgForのどちらが先になるでしょうか。 NgIfはNgForの効果をキャンセルできますか?もしそうなら(そしてそうあるべきだと思われる)、Angularは他の構造的な指示を取り消す能力をどのように一般化すべきですか?

これらの質問に対する簡単な答えはありません。複数の構造指令を禁止することは、それらを根拠のないものにします。このユースケースには簡単な解決策があります。 * ngFor 要素をラップするコンテナ要素に * ngIf を追加します。片方または両方の要素を 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>
2
Alireza
<div *ngFor="let thing of show ? stuff : []">
  {{log(thing)}}
  <span>{{thing.name}}</span>
</div>
1
Prashant Borde
<!-- 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>
0
Rajiv

hTMLで:

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

cSSで:

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

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

0
Pradip Patil

同じHTML要素に * ngFor ngIf の両方を適用するには、(templateの代わりにng-templateを使用することもできます。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']

注意:

ng-templateタグの代わりにtemplateタグのみを使用することの警告は、それがいくつかの場所でStaticInjectionErrorを投げるということです。

0