581

我在尝试使用 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向上移动一个级别,但是对于像在 a 中循环列表项这样的场景,如果集合为空,ul我最终会得到一个空的,或者我的lili的,或者我的 s 被包装在冗余容器元素中。

这个plnkr的例子。

注意控制台错误:

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

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

4

17 回答 17

821

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><template>在 Angular v4 之前)允许做同样的事情,但使用不同的语法,这令人困惑,不再推荐

<ng-template [ngIf]="show">
  <div *ngFor="let thing of stuff">
    {{log(thing)}}
    <span>{{thing.name}}</span>
  </div>
</ng-template>
于 2016-09-19T05:27:39.663 回答
97

正如每个人都指出的那样,即使在单个元素中有多个模板指令在 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>
于 2016-07-14T16:22:04.137 回答
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>

列表为空时没有空<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>

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

于 2016-01-07T15:59:41.033 回答
9

在 html 中:

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

在 CSS 中:

.disabled-field {
    pointer-events: none;
    display: none;
}
于 2019-04-19T05:14:38.200 回答
6

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

.hidden {
    display: none;
}

<div [class.hidden]="!show" *ngFor="let thing of stuff">
    {{log(thing)}}
    <span>{{thing.name}}</span>
</div>
于 2016-08-15T17:25:22.103 回答
6

您不能在 Angular 中对同一个元素使用多个Structural Directive,这会造成严重的混淆和结构,因此您需要将它们应用到 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>
于 2017-07-02T08:26:52.227 回答
5

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

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

于 2016-01-07T14:48:51.857 回答
3

您还可以使用ng-template(而不是模板。有关使用模板标签的注意事项,请参阅注释)在同一个 HTML 元素上应用*ngForngIf 。这是一个示例,您可以将*ngIf 和 *ngFor用于角度表中的相同tr元素。

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

哪里fruiArray = ['apple', 'banana', 'mango', 'pineapple']

笔记:

template仅使用标签而不是标签的警告ng-template是它会StaticInjectionError在某些地方抛出。

于 2019-02-01T10:39:42.670 回答
3

下表仅列出了设置了“初学者”值的项目。需要 the*ngFor和 the*ngIf以防止 html 中出现不需要的行。

最初有*ngIf*ngFor在同一个<tr>标​​签上,但不起作用。添加了一个<div>for*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>
于 2016-12-19T19:59:48.240 回答
3

我用这个解决了我的问题

<ng-container *ngFor="let item of menuItems">
  <li *ngIf="canShowItem(item)"></li>
</ng-container>
于 2021-10-12T16:40:22.037 回答
3

更新到 angular2 beta 8

现在,从 angular2 beta 8 开始,我们可以在同一组件上使用*ngIf请参见此处*ngFor

备用:

有时我们不能在另一个内部使用 HTML 标记,例如 in trth( table) 或 in li( ul)。我们不能使用另一个 HTML 标签,但我们必须在相同的情况下执行一些操作,这样我们才能<template>以这种方式使用 HTML5 特征标签。

ngFor 使用模板:

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

ngIf 使用模板:

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

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

于 2016-04-04T11:52:02.260 回答
2

我不想将我的 with或 use包装*ngFor到另一个中,所以我创建了一个名为的管道:div*ngIf[ngClass]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>
于 2020-10-23T08:03:21.243 回答
1
<div *ngFor="let thing of show ? stuff : []">
  {{log(thing)}}
  <span>{{thing.name}}</span>
</div>
于 2018-01-16T10:13:10.570 回答
0

您不能在同一元素上使用多个结构指令。将您的元素包装进去ng-template并在那里使用一个结构指令

于 2018-11-17T16:28:57.463 回答
0

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

于 2016-11-02T10:55:06.623 回答
0

其他解决方案可能是在您不想显示的情况下在 for 循环中放置一个空数组

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

其中“stuff”是“事物”的数组,“显示”布尔值是否显示或不显示内容

于 2020-09-02T14:24:54.507 回答
0

您可以通过检查数组长度以另一种方式执行此操作

<div *ngIf="stuff.length>0">
  <div *ngFor="let thing of stuff">
    {{log(thing)}}
    <span>{{thing.name}}</span>
  </div>
</div>
于 2020-04-14T07:45:21.030 回答