同じ要素に対して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
、冗長なコンテナ要素でラップされた場合はli
sになります。
これでの例 plnkr 。
コンソールエラーに注意してください。
EXCEPTION: TypeError: Cannot read property 'name' of null in [{{thing.name}} in ShellComponent@5:12]
私は何か悪いことをしていますか、これはバグですか?
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>
1つの要素に複数のテンプレートディレクティブを設定することはAngular 1.xでは機能しますが、Angular 2では許可されていません。 https:// github .com /角度/角度/ issue/7315
解決策は、プレースホルダーとして<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年現在、Angular v6では<ng-container>
の代わりに<template>
を使用することが推奨されています
だからここで更新された答えです。
<ng-container *ngFor="let nav_link of defaultLinks" >
<li *ngIf="nav_link.visible">
.....
</li>
</ng-container>
もう1つ解決策があります。
[hidden]
の代わりに*ngIf
を使用してください
<div [hidden]="!show" *ngFor="let thing of stuff">
違いは、*ngIf
はDOMから要素を削除するのに対し、[hidden]
はdisplay:noneを設定することで実際にはCSS
スタイルで再生されることです。
@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>
この解決策はまた、余分な/冗長なコンテナ要素を導入しない。
同じ要素にngFor
とngIf
を含めることはできません。あなたの例のトグルがクリックされるまであなたがすることができるのはあなたがngFor
であなたが使っている配列を生成することを延期することです。
これがあなたがそれをすることができる基本的な(素晴らしいではない)方法です: http://plnkr.co/edit/Pylx5HSWIZ7ahoC7wT6P
以下の表は、「初心者」の値が設定されている項目のみを示しています。 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>
これは機能しますが、要素はまだDOM内にあります。
.hidden{
display: none;
}
<div [class.hidden]="!show" *ngFor="let thing of stuff">
{{log(thing)}}
<span>{{thing.name}}</span>
</div>
今度はangular2 beta 8以降、同じコンポーネント ここを参照 で*ngIf
と*ngFor
を使うことができます。
代わりの:
tr
、th
(table
)、li
(ul
)のように、HTMLタグを他のHTMLタグの内側で使用できないことがあります。別のHTMLタグを使用することはできませんが、同じ状況で何らかのアクションを実行する必要があるため、この方法でHTML5機能タグ<template>
を使用できます。
<template ngFor #abc [ngForOf]="someArray">
code here....
</template>
<template [ngIf]="show">
code here....
</template>
Angular2 ここを参照 の構造化命令の詳細については。
同じ要素の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>
<div *ngFor="let thing of show ? stuff : []">
{{log(thing)}}
<span>{{thing.name}}</span>
</div>
<!-- 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>
hTMLで:
<div [ngClass]="{'disabled-field': !show}" *ngFor="let thing of stuff">
{{thing.name}}
</div>
cSSで:
.disabled-field {
pointer-events: none;
display: none;
}
同じ要素に複数の構造化命令を使用することはできません。要素をng-template
で囲み、そこに1つの構造ディレクティブを使用します
同じ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
を投げるということです。