* 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>
ngFor
和ngIf
在同一个元素上。 你可以做的是暂停填充你在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
。
备用:
有时我们不能在tr
, th
( table
)或者li
( ul
)中使用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>