首页 文章

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

提问于
浏览
270

我'm having a problem with trying to use Angular' s *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 ,或者我的 li 被包装在冗余的容器元素中 .

这个例子plnkr .

请注意控制台错误:

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

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

12 回答

  • 1

    这将工作,但元素仍将在DOM中 .

    .hidden{
        display: none;
    }
    

    <div [class.hidden]="!show" *ngFor="let thing of stuff">
      {{log(thing)}}
      <span>{{thing.name}}</span>
    </div>
    
  • 2

    Angular v2不支持同一元素上的多个结构指令 .
    作为一种解决方法,使用 <ng-container> 元素,允许您为每个结构指令使用单独的元素,但它是 not stamped to the 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>
    
  • 26

    我还有一个解决方案 .

    使用 [hidden] 而不是 *ngIf

    <div [hidden]="!show" *ngFor="let thing of stuff">
    

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

  • 5

    已更新至angular2 beta 8

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

    备用:

    有时我们不能在 trthtable )或 liul )中使用HTML标签 . 我们不能使用其他HTML标记,但我们必须在相同的情况下执行某些操作,因此我们可以通过这种方式使用HTML5功能标记 <template> .

    ng使用模板:

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

    ngIf使用模板:

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

    有关angular2 see here中结构指令的更多信息 .

  • 3

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

  • 29
    <div *ngFor="let thing of show ? stuff : []">
      {{log(thing)}}
      <span>{{thing.name}}</span>
    </div>
    
  • 3

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

    2016 angular 2 beta

    解决方案是使用 <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年角度v6建议使用 <ng-container> 而不是 <template>

    所以这是更新的答案 .

    <ng-container *ngFor="let nav_link of defaultLinks" >
       <li *ngIf="nav_link.visible">
           .....
       </li> 
    </ng-container>
    
  • 2

    你不能在同一个元素上使用Angular中的多个 Structural Directive ,它会产生一个糟糕的混淆和结构,所以你需要在两个独立的嵌套元素中应用它们(或者你可以使用 ng-container ),从Angular团队中读取这个语句:

    每个主机元素的一个结构指令有一天,你会想要重复一个HTML块,但只有在特定条件为真时才会重复 . 您将尝试将* ngFor和* ngIf放在同一主机元素上 . Angular不会让你 . 您可以只对元素应用一个结构指令 . 原因很简单 . 结构指令可以与宿主元素及其后代做复杂的事情 . 当两个指令声称属于同一个主机元素时,哪一个优先?首先应该是NgIf还是NgFor? NgIf可以取消NgFor的效果吗?如果是这样(似乎应该如此),Angular应如何概括取消其他结构指令的能力?这些问题没有简单的答案 . 禁止多个结构指令使它们没有实际意义 . 这个用例有一个简单的解决方案:将* ngIf放在包装* ngFor元素的容器元素上 . 一个或两个元素可以是ng-container,因此您不必引入额外的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>
    
  • 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>
    
  • 430

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

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

  • 21

    正如@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 discussion (4792)也提出了另一个使用 <template> 的解决方案(下面我使用你的原始标记 - 使用 <div> s):

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

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

  • 0

    下表仅列出了设置了"beginner"值的项目 . 需要 *ngFor*ngIf 来防止html中不需要的行 .

    最初在 <tr> 标签上有 *ngIf*ngFor ,但不起作用 . 为 *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>
    

相关问题