*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移动到一个级别,但对于像在ul中循环列表项这样的场景,如果集合是空的,我最终会得到一个空的li,或者我的lis包装在多余的容器元素中。

示例:plnkr

注意控制台错误:

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

是我做错了什么还是这是个bug?

428502 次浏览

你不能在同一个元素上使用ngForngIf。您可以做的是推迟填充您在ngFor中使用的数组,直到单击示例中的切换。

这里有一个基本的(不是很好的)方法:http://plnkr.co/edit/Pylx5HSWIZ7ahoC7wT6P

正如@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>元素也不存在(正如预期的那样)。

填充列表时,没有多余的容器元素。

@Zyzle在他的评论中提到的Github讨论(4792)也提出了另一个解决方案,使用<template>(下面我使用您原始的标记&破折号;使用# EYZ1s):

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

这个解决方案也没有引入任何额外/冗余的容器元素。

更新到angular2 beta 8

现在从angular2 beta 8开始,我们可以在同一个组件在这里看到的上使用*ngIf*ngFor

可选:

有时我们不能在另一个标签中使用HTML标签,如trth (table)或li (ul)。我们不能使用另一个HTML标签,但我们必须在相同的情况下执行一些操作,因此我们可以以这种方式使用HTML5功能标签<template>

ngFor使用模板:

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

ngIf使用模板:

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

有关angar2 在这里看到的中的结构指令的更多信息。

正如每个人都指出的那样,即使在一个元素中有多个模板指令在angular 1中也是有效的。x在Angular 2中是不允许的。 你可以在这里找到更多信息:https://github.com/angular/angular/issues/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>

这将起作用,但元素仍然在DOM中。

.hidden {
display: none;
}

<div [class.hidden]="!show" *ngFor="let thing of stuff">
\{\{log(thing)}}
<span>\{\{thing.name}}</span>
</div>
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>

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

下表仅列出具有“初学者”值集的项目。 需要*ngFor*ngIf来防止html中不需要的行

最初有*ngIf*ngFor在同一个<tr>标签上,但不工作。 为*ngFor循环添加了<div>,并将*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>

在Angular中,你不能在同一个元素上使用多个Structural Directive,这会造成严重的混乱和结构,所以你需要在两个独立的嵌套元素中应用它们(或者你可以使用ng-container),阅读Angular团队的声明:

每个宿主元素一个结构指令

有一天你会想要重复一个HTML块,但只有当a 特殊条件为真。您将尝试同时放置* ngFor和 同一个主机元素上的* ngIf。Angular不会让你这么做。你可能 对一个元素只应用一个结构指令 原因很简单。结构性指令可以做复杂的事情 使用宿主元素及其后代。当两条指令摆在面前 声明同一个主机元素,哪个优先?哪一个 应该先用NgIf还是NgFor?NgIf可以取消这个效果吗 NgFor?如果是这样(看起来应该是这样),该怎么做呢 Angular泛化了其他结构的取消功能 指令?< / p > 这些问题没有简单的答案。禁止多 结构性指令使它们毫无意义。有一个简单的解决办法 这个用例:将* ngIf放在容器元素上 * ngFor < /强>元素。一个或两个元素都可以是ng-container,这样就不必引入额外的HTML级别。

所以你可以使用ng-container (Angular4)作为包装器(将从dom中删除),如果你有class或其他一些属性,可以使用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>

你不能在同一个元素上使用多个结构指令。将元素包装在ng-template中,并在那里使用一个结构指令

您也可以使用ng-template(而不是template。在同一个HTML元素上应用* ngForngIf,请参阅使用模板标签的注意事项)。下面是一个例子,你可以用*ngIf和*ngFor替换angular表中的tr元素。

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

# EYZ0的地方。

注意:

只使用template标记而不是ng-template标记的警告是,它会在某些地方抛出StaticInjectionError

在html中:

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

在css中:

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

你可以用另一种方法检查数组长度

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

另一个解决方案可能是在你的for循环中放一个空数组,如果你不想显示它

<div *ngFor="let thing of show ? stuff : []">

在“stuff"是一个“东西”的数组;和“;show"是否显示内容的布尔值

我不想用*ngIf[ngClass]将我的*ngFor包装成另一个div,所以我创建了一个名为show的管道:

show.pipe.ts

export class ShowPipe implements PipeTransform {
transform(values: any[], show: boolean): any[] {
if (!show) {
return[];
}
return values;
}
}

any.page.html

<table>
<tr *ngFor="let arr of anyArray | show : ngIfCondition">
<td>\{\{arr.label}}</td>
</tr>
</table>

我用这个解决我的问题

<ng-container *ngFor="let item of menuItems">
<li *ngIf="canShowItem(item)"></li>
</ng-container>