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

我知道简单的解决scheme是将*ngIf移动到一个水平,但对于在ul循环显示列表项目的场景,如果集合是空的,我最终会得到一个空li ,或者我的包裹被包装在冗余容器中元素。

示例在这个plnkr 。

注意控制台错误:

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

我做错了什么或者这是一个错误?

Angular2在同一个元素上不支持多个结构指令。
作为解决方法,使用允许您为每个结构指令使用单独的元素的<ng-container>元素,但是它不会被标记到DOM

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

Angular4中的<template><ng-template> )可以做同样的事情,但使用的语法不同而且不再推荐

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

正如@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>另一个解决scheme(下面我使用你的原始标记 – 使用<div> s):

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

该解决scheme也不会引入任何额外/冗余的容器元素。

正如大家所指出的,即使在单个元素中有多个模板指令工作在angular度1.x中,Angular 2也不允许这样做。您可以从这里find更多信息: https : //github.com/angular/angular/issues/ 7315

另一种方法是使用<template>作为占位符,所以代码就像这样

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

但由于某些原因,在2.0.0-rc.4中无法使用,在这种情况下,您可以使用此function

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

ngForngIf在同一个元素上。 你可以做的是暂停填充你在ngFor使用的数组,直到点击你的例子中的切换。

这是一个基本的(不是很好)的方式,你可以做到这一点: http : //plnkr.co/edit/Pylx5HSWIZ7ahoC7wT6P

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

原来有*ngIf*ngFor在相同的<tr>标签,但不起作用。 为*ngFor循环添加了一个<div> ,并将*ngFor放在<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

现在,从angular2 beta 8开始,我们可以在相同的组件上使用*ngIf*ngFor

备用:

有时我们不能在trthtable )或者liul )中使用HTML标签。 我们不能使用另一个HTML标签,但是我们必须在相同的情况下执行一些操作,所以我们可以通过这种方式来使用HTML5特征标签<template>

ngFor使用模板:

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

ng如果使用模板:

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

有关angular2中结构指令的更多信息, 请参阅此处 。

你不能在同一个元素中使用Angular中的多个Structural Directive ,它会产生一个错误的混淆和结构,所以你需要将它们应用于两个单独的嵌套元素(或者你可以使用ng-container ),从Angular球队:

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

有一天,你会想重复一个HTML块,但只有当特定的条件是真实的。 您将尝试将* ngFor* ngIf放在同一主机元素上。 angular度不会让你。 你可以只应用一个结构指令给一个元素。

原因是简单。 结构指令可以与宿主元素及其后代做复杂的事情。 当两个指令声明相同的主机元素,哪一个优先? NgIf还是NgFor呢? NgIf可以取消NgFor的效果吗? 如果是这样(似乎应该如此),Angular应该如何推广取消其他结构性指令的能力?

这些问题没有简单的答案。 禁止多项结构性指令使得这些指令毫无意义。 这个用例有一个简单的解决scheme:将* ngIf放在包装* ngFor元素的容器元素上。 一个或两个元素可以是一个ng容器,所以你不必引入额外的HTML级别。

所以你可以使用ng-container (Angular4)作为包装器(将从dom中删除)或div或span,如果你有class或者其他一些属性如下:

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