Angular 2 ngFor vs Angular 1 ng-repeat

1

一直想写关于 Angular 1.x 与 Angular 2.x (Angular 4.x 已发布) 区别的文章,方便 Angular 1.x 的用户快速的过渡到 Angular 2.x。在浏览文章的时候,发现 Todd Motto 大神,已经写了相关的系列文章。英文好的同学,建议直接阅读 From ng-repeat in Angular 1.x to ngFor in Angular 2 原文哈,因为我并不打算完整地翻译。废话不多说,接下来我们开始进入正题。

目录

  • Angular 1.x

    • Using ng-repeat

    • Using $index and track by

  • Angular 2.x

    • Using ngFor

    • Using index and trackBy

Angular 1.x

Using ng-repeat

在使用 ng-repeat 指令之前,我们需要组件相关的控制器中设置添加初始数据,具体如下:

const app = {
  controller() {
    this.groceries = [{
      id: 0, label: 'Butter'
    },{
      id: 1, label: 'Apples'
    },{
      id: 2, label: 'Paprika'
    },{
      id: 3, label: 'Potatoes'
    },{
      id: 4, label: 'Oatmeal'
    },{
      id: 5, label: 'Spaghetti'
    },{
      id: 6, label: 'Pears'
    },{
      id: 7, label: 'Bacon'
    }];
  }
};

angular
  .module('app')
  .component('app', app);

接下来我们在组件模板中,使用 ng-repeat 指令显示我们上面定义的数据:

const app = {
  template: `
    <div>
      Grocery selected: {{ $ctrl.selectedGrocery.label }}
      <ul>
        <li ng-repeat="grocery in $ctrl.groceries">
          <a href="" ng-click="$ctrl.selectGrocery(grocery);">
            {{ grocery.label }}
          </a>
        </li>
      </ul>
    </div>
  `,
  ...
};

Using $index and track by

$index 表示数组中每一项的索引值,除了 $index 之外,ng-repeat 还导出$first$last$even$odd 等属性,详细信息可以查看 - ngRepeat官方文档。接下来,我们先来看一下 $index 示例:

const app = {
  template: `
    ...
        <li ng-repeat="grocery in $ctrl.groceries">
          <a href="" ng-click="$ctrl.selectGrocery(grocery);">
            {{ grocery.label }} {{ $index }}
          </a>
        </li>
    ...
  `,
  ...
};

在设置 ng-repeat 初始数据时,你可能已经注意到了, this.groceries 数组中的每一项都有一个唯一的 id 属性,基于这个唯一的属性,我们可以通过 ng-repeat 指令提供的 track by 表达式,进行页面性能优化,防止 Angular 重新渲染整个列表。即不是每次销毁和重建列表相关的 DOM 树,而是重新渲染那些需要更新的 DOM 元素。

const app = {
  template: `
    ...
        <li ng-repeat="grocery in $ctrl.groceries track by grocery.id">
          <a href="" ng-click="$ctrl.selectGrocery(grocery);">
            {{ grocery.label }} {{ $index }}
          </a>
        </li>
    ...
  `,
  ...
};

此外 track by 也支持函数表达式:

const app = {
  template: `
    ...
        <li ng-repeat="grocery in $ctrl.groceries track by trackByGrocery(grocery)">
          <a href="" ng-click="$ctrl.selectGrocery(grocery);">
            {{ grocery.label }} {{ $index }}
          </a>
        </li>
    ...
  `,
  ...
};

Angular 2.x

Angular 2.x 中不存在 ng-repeat 指令,取而代之的是 ngFor 指令。它们的语法非常相似,但需要注意的一点在遍历集合是,Angular 2 使用 of 代替了 in

Using ngFor

interface Grocery {
  id: number;
  label: string;
}

export default class AppComponent {
  public groceries: Grocery[];
  public selectedGrocery: Grocery;
  
  constructor() {
    this.groceries = [{
      id: 0, label: 'Butter'
    },{
      id: 1, label: 'Apples'
    },{
      id: 2, label: 'Paprika'
    },{
      id: 3, label: 'Potatoes'
    },{
      id: 4, label: 'Oatmeal'
    },{
      id: 5, label: 'Spaghetti'
    },{
      id: 6, label: 'Pears'
    },{
      id: 7, label: 'Bacon'
    }];
    this.selectGrocery(this.groceries[0]);
  }
  selectGrocery(grocery: Grocery) {
    this.selectedGrocery = grocery;
  }
}

设置好初始化数据,接下来我们来使用 ngFor 指令,需要注意的是在模板中,我们需要使用 let 关键字创建局部作用域,具体示例如下:

import { Component } from '@angular/core';

interface Grocery {
  id: number;
  label: string;
}

@Component({
  selector: 'exe-app',
  template: `
    <div>
      Grocery selected: {{ selectedGrocery.label }}
      <ul>
        <li *ngFor="let grocery of groceries;">
          <a href="#" (click)="selectGrocery(grocery);">
            {{ grocery.label }}
          </a>
        </li>
      </ul>
    </div>
  `
})
export class AppComponent {
  public groceries: Grocery[];
  public selectedGrocery: Grocery;

  constructor() {
    this.groceries = [{
      id: 0, label: 'Butter'
    },{
      id: 1, label: 'Apples'
    },{
      id: 2, label: 'Paprika'
    },{
      id: 3, label: 'Potatoes'
    },{
      id: 4, label: 'Oatmeal'
    },{
      id: 5, label: 'Spaghetti'
    },{
      id: 6, label: 'Pears'
    },{
      id: 7, label: 'Bacon'
    }];
    this.selectGrocery(this.groceries[0]);
  }
  selectGrocery(grocery: Grocery) {
    this.selectedGrocery = grocery;
  }
}

细心的读者,可能会注意到模板中 *ngFor 语法,ngFor 指令前面的 * 号是语法糖,表示使用 <template> 元素。详细内容,我们会在 "我有话说" 章节介绍。

Using index and trackBy

在 Angular 1.x 中我们可以直接 $index 访问到列表项的索引值,但在 Angular 2 中,我们使用 index 之前,我们必须先把它赋值给其它变量,具体示例如下:

@Component({
  selector: 'exe-app',
  template: `
    <div>
      Grocery selected: {{ selectedGrocery.label }}
      <ul>
        <li *ngFor="let grocery of groceries; let i = index;">
          <a href="#" (click)="selectGrocery(grocery);">
            {{ grocery.label }} {{ i }}
          </a>
        </li>
      </ul>
    </div>
  `
})
export default class AppComponent {...}

介绍完 ngFor 中的 index 用法,接下来我们来看一下 trackBy 用法。在 Angular 2 中不支持 Angular 1.x 中的 track by x 如:track by grocery.id 语法, trackBy 只支持函数。我们来看一下具体示例:

@Component({
  selector: 'exe-app',
  template: `
    <div>
      Grocery selected: {{ selectedGrocery.label }}
      <ul>
        <li *ngFor="let grocery of groceries; let i = index; trackBy: trackByGrocery;">
          <a href="#" (click)="selectGrocery(grocery);">
            {{ grocery.label }} {{ i }}
          </a>
        </li>
      </ul>
    </div>
  `
})
export default class AppComponent {
  ...
  trackByGrocery: (index: number, grocery: Grocery): number => grocery.id;
  ...
}

完整示例如下:

import { Component } from '@angular/core';

interface Grocery {
  id: number;
  label: string;
}

@Component({
  selector: 'exe-app',
  template: `
    <div>
      Grocery selected: {{ selectedGrocery.label }}
      <ul>
        <li *ngFor="let grocery of groceries; let i = index; trackBy: trackByGrocery;">
          <a href="#" (click)="selectGrocery(grocery);">
            {{ grocery.label }} {{ i }}
          </a>
        </li>
      </ul>
    </div>
  `
})
export class AppComponent {
  public groceries: Grocery[];
  public selectedGrocery: Grocery;

  constructor() {
    this.groceries = [{
      id: 0, label: 'Butter'
    }, {
      id: 1, label: 'Apples'
    }, {
      id: 2, label: 'Paprika'
    }, {
      id: 3, label: 'Potatoes'
    }, {
      id: 4, label: 'Oatmeal'
    }, {
      id: 5, label: 'Spaghetti'
    }, {
      id: 6, label: 'Pears'
    }, {
      id: 7, label: 'Bacon'
    }];
    this.selectGrocery(this.groceries[0]);
  }

  selectGrocery(grocery: Grocery) {
    this.selectedGrocery = grocery;
  }

  trackByGrocery(index: number, grocery: Grocery): number {
    return grocery.id
  }
}

我有话说

1.Angular 2 指令有几种分类?

在 Angular 2 指令分为三类:

  • 组件(Component directive):用于构建UI组件,继承于 Directive 类

  • 属性指令(Attribute directive): 用于改变组件的外观或行为

  • 结构指令(Structural directive): 用于动态添加或删除DOM元素来改变DOM布局

详细内容请参考 - Angular 2 Directive

2.Angular 2 中的模板语法有哪一些?

Angular 2 中没有 Angular 1.x ng-clickng-bindng-show 等指令,取而代之的是新的模板语法。

2.1 属性绑定:

<show-title [title]="title"></show-title>

2.2 事件绑定:

<a href="#" (click)="selectGrocery(grocery);">
    {{ grocery.label }} {{ i }}
</a>

详细内容请参考 - Angular 2 template syntax & common directives


如果觉得我的文章对你有用,请随意赞赏

你可能感兴趣的

去StackOverflow · 2017年06月16日

ng-repeat可以
Iterating over object properties

<div ng-repeat="(key, value) in myObj"> ... </div>

ngFor好像没有这个

回复

0

嗯目前不支持,可以在组件类中进行数据处理,另外一种可以自定义指令实现这个功能哈。有兴趣的话,可以浏览一下 [Angular 4.x NgForOf] (https://segmentfault.com/a/11...

semlinker 作者 · 2017年06月16日
1
semlinker 作者 · 2017年06月16日
0

ok 下班了 回家看

去StackOverflow · 2017年06月16日
chenyulun · 2017年08月01日

不使用trackBy的情况,ng1遍历[1,2,1]会报错,ng4不加并不会报错,ng4中trackBy: trackByGrocery;什么场景下用,能解决什么问题?

回复

0
semlinker 作者 · 2017年08月01日
0

怎么就提升列表性能了?难道和react的key类似?

chenyulun · 2017年08月01日
载入中...