*ngIf和*ngFor在同一元素上导致错误

gar*_*hdn 410 angular-ng-if ngfor angular

我在尝试使用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);
  }

}
Run Code Online (Sandbox Code Playgroud)

我知道简单的解决方案是*ngIf向上移动一个级别但是对于像a循环列表项的情况一样ul,li如果集合为空,我最终将为空,或者我的lis包装在冗余容器元素中.

这个plnkr的例子.

请注意控制台错误:

EXCEPTION: TypeError: Cannot read property 'name' of null in [{{thing.name}} in ShellComponent@5:12]
Run Code Online (Sandbox Code Playgroud)

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

Gün*_*uer 611

Angular v2不支持同一元素上的多个结构指令.
作为一种解决方法,使用<ng-container>允许您为每个结构指令使用单独元素的元素,但不会将其标记为DOM.

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

<ng-template>(<template>在Angular v4之前)允许执行相同但具有不同语法的混淆且不再推荐的语法

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

  • 只需在`<ng-container>`元素放置`ngFor`,在`<div>`放置`ngIf`.你也可以有两个嵌套的`<ng-container>`包装`<div>`.`<ng-container>`只是一个不会添加到DOM的辅助元素. (18认同)
  • 当我们必须在*ngFor中使用*ngIf时,代码将如何?即IF条件将基于循环元素的值. (7认同)
  • 非常感谢.令人惊讶的是仍未记录:https://github.com/angular/angular.io/issues/2303 (5认同)
  • 我建议使用`<ng-container>`.它的行为与`<template>`相同,但允许对结构指令使用"普通"语法. (3认同)
  • [文档说](https://angular.io/guide/structural-directives):“每个主机元素一个结构指令”:“此用例有一个简单的解决方案:将* ngIf放在包装了容器的容器元素上* ngFor元素。” -只是重申 (2认同)

ima*_*era 58

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

2016角2 beta

解决方案是使用<template>占位符,所以代码就像这样

<template *ngFor="let nav_link of defaultLinks"  >
   <li *ngIf="nav_link.visible">
       .....
   </li>
</template>
Run Code Online (Sandbox Code Playgroud)

但由于某种原因,上述情况不起作用,2.0.0-rc.4你可以使用它

<template ngFor let-nav_link [ngForOf]="defaultLinks" >
   <li *ngIf="nav_link.visible">
       .....
   </li> 
</template>
Run Code Online (Sandbox Code Playgroud)

更新的答案2018

随着更新,现在2018年角度v6建议使用<ng-container>而不是<template>

所以这是更新的答案.

<ng-container *ngFor="let nav_link of defaultLinks" >
   <li *ngIf="nav_link.visible">
       .....
   </li> 
</ng-container>
Run Code Online (Sandbox Code Playgroud)


Ajm*_*sha 41

我还有一个解决方案.

[hidden]而不是 *ngIf

<div [hidden]="!show" *ngFor="let thing of stuff">
Run Code Online (Sandbox Code Playgroud)

区别在于*ngIf将从DOM中删除元素,而[hidden]实际上CSS通过设置display:none来播放样式


Mar*_*cok 29

正如@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>
Run Code Online (Sandbox Code Playgroud)

<li>列表为空时没有空元素.甚至<ul>元素也不存在(如预期的那样).

填充列表时,没有冗余容器元素.

@Zyzle在他的评论中提到的github讨论(4792)也提出了另一个使用的解决方案<template>(下面我使用你的原始标记 - 使用<div>s):

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

此解决方案也不会引入任何额外/冗余容器元素.


小智 11

我用这个解决了我的问题

<ng-container *ngFor="let item of menuItems">
  <li *ngIf="canShowItem(item)"></li>
</ng-container>
Run Code Online (Sandbox Code Playgroud)


moj*_*ani 9

在 html 中:

<div [ngClass]="{'disabled-field': !show}" *ngFor="let thing of stuff">
    {{thing.name}}
</div>
Run Code Online (Sandbox Code Playgroud)

在 CSS 中:

.disabled-field {
    pointer-events: none;
    display: none;
}
Run Code Online (Sandbox Code Playgroud)


ron*_*192 6

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

.hidden {
    display: none;
}
Run Code Online (Sandbox Code Playgroud)
<div [class.hidden]="!show" *ngFor="let thing of stuff">
    {{log(thing)}}
    <span>{{thing.name}}</span>
</div>
Run Code Online (Sandbox Code Playgroud)


Zyz*_*zle 5

您不能在同一元素上使用ngForngIf。您可以做的是推迟填充正在使用的阵列,ngFor直到单击示例中的切换按钮为止。

这是您可以执行的基本(不太好)方法:http : //plnkr.co/edit/Pylx5HSWIZ7ahoC7wT6P


Ali*_*eza 5

您不能Structural Directive在Angular中对同一个元素使用多个元素,这会造成混乱和结构混乱,因此您需要将它们应用于2个单独的嵌套元素中(或者可以使用ng-container),请从Angular团队阅读以下声明:

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

总有一天,您只想在满足特定条件时才重复HTML块。您将尝试将* ngFor* ngIf放在同一宿主元素上。Angular不会让你。您只能对一个元素应用一个结构指令。

原因是简单。结构化指令可以对宿主元素及其后代进行复杂的处理。当两个指令对同一宿主元素提出要求时,哪个优先?NgIf或NgFor应该先走?NgIf是否可以取消NgFor的效果?如果是这样(看起来应该是这样),Angular应该如何概括取消其他结构性指令的能力?

这些问题没有简单的答案。禁止多个结构性指令使它们无济于事。对于这种用例有一个简单的解决方案:将* ngIf放在包装* ngFor元素的容器元素上 。一个或两个元素都可以是ng容器,因此您不必引入额外的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>
Run Code Online (Sandbox Code Playgroud)