新新新手阅读 Angular 文件 - ngFor(1) - Day19

本文内容

阅读有关 Angular 中有 ngFor 语法的笔记内容。

ngFor 在干嘛的?

它用来遍历某个集合(比如: 某一个阵列)中的每一个元素,并在遍历的当下为该元素渲染出属於它的样板到画面上。

ngFor用法

[View]

<ul>
  <li *ngFor="let user of userInfo; index as i">{{ i }} - {{ user.name }}</li>
</ul>

[TypeScript]

export class AppComponent {
  userInfo = [{ name: 'Jared' }, { name: 'Tom' }, { name: 'Max' }];
}

上面的范例可以看到我们在 li 元素上加上了 ngFor 的内容,用以遍历 userInfo 的阵列内容,此时,当该阵列中的元素被遍历到的时候,就会为该元素产出一个 li 元素,所以, userInfo 是一个有三个元素的阵列,故画面上会被产出三个 li 元素。

shorthand form *ngFor

以上范例的写法其实是 ngFor 语法的简写,那在官方文件有说明以上范例中的 ngFor 实际上 Angular 会把它翻译成这样

<ng-template ngFor let-user [ngForOf]="userInfo" let-i="index">
  <li> {{ user.name }}</li>
</ng-template>

当 Angular 遇到 ngFor 前面的星号 *(asterisk) ,它就会将产出一个 ng-template 将 ngFor 所在的元素包起来(以这边的范例是 li),接着,其他的属性对应的对象,我列在下面
ngFor: 对应到原本的 ngFor
[ngForof]="userInfo": 这句话的意思是要被遍历的对象为 userInfo
let-user: 对应到原本的 *ngFor="let user of userInfo" 中的 user
let-i="index": 对应到原本的 let index as i

ngFor 所产生出的 local variables

在官方文件上还有特别列出了 ngFor 会产出的区域变数,

<li *ngFor="let user of users; index as i; first as isFirst">
  {{i}}/{{users.length}}. {{user}} <span *ngIf="isFirst">default</span>
</li>

index: number: 当下被遍历到的元素在该所属阵列中的位置
count: number: 被遍历对象的元素总数,以上面的范例为例的话,就是 users.length
first: boolean: 当被遍历的当下元素是该被遍历对象的第一个元素时,会回传 true
last: boolean: 当被遍历的当下元素是该被遍历对象的最後一个元素时,会回传 true
even: boolean: 当被遍历的当下元素是该被遍历对象的偶数元素时,会回传 true
odd: boolean: 当被遍历的当下元素是该被遍历对象的奇数元素时,会回传 true

改变 ngFor 新增/删减遍历元素的机制

当改变 ngFor 所遍历目标的内容的时候, Angular 会以以下机制来处理 DOM 的内容

  1. 新增遍历目标的内容: Angular 将会产出一个新的实例样板,并将它渲染到画面上。
  2. 删除遍历目标的内容: Angular 将会删除一个指定的实例,并将其从画面上拔除。
  3. 当遍历目标内的元素的位置被调换,则它们在画面上的 DOM 位置也会被对调到相对应的位置。

ngFor 的 trackBy 属性,来优化 ngFor 渲染机制

在许多产品中,很常会有当使用者送出某个更新後的内容後,再从 server 取回更新後的内容,接着再将它渲染到画面上。
这边做个简单的范例
[View]

<ul>
  <li *ngFor="let user of userInfo; index as i">{{ i }} - {{ user.name }}</li>
</ul>
<button (click)="getUserInfoArray()">getUserInfo</button>

[TypeScript]

export class AppComponent {
  userInfo = [{ name: 'Jared' }, { name: 'Tom' }, { name: 'Max' }];

  getUserInfoArray() {
    this.userInfo = [
      { name: 'Jared' },
      { name: 'Tom' },
      { name: 'Max' },
      { name: 'Wendy' },
      { name: 'Mars' },
      { name: 'Suns' },
    ];
  }
}

以上范例的操作结果

可以看到以上的影片,当按下按钮,会模仿重新 GET 後的阵列内容并将其渲染到画面上。接着,在 DOM 上可以看到 ul 和 li 的 DOM 元素全部都有闪一次,有闪的部分就代表它被重新渲染到画面上了,由此可知, ul 和 li 全部都被重新渲染了。

但事实上并不是全部的阵列的内容都是新的,只有几个元素是新增的,这样的话,连那些重复的元素都被重新渲染的话,岂不很浪费浏览器的资源吗~~
所以,我们可以引用 ngFor 的 trackBy 属性来优化以上的情境,来改写一下上面的范例
[View]

<ul>
  <li *ngFor="let user of userInfo; index as i; trackBy: trackByItems">{{ i }} - {{ user.name }}</li>
</ul>
<button (click)="getUserInfoArray()">getUserInfo</button>

[TypeScript]

export class AppComponent {
  userInfo = [{ name: 'Jared' }, { name: 'Tom' }, { name: 'Max' }];
  
  trackByItems(index, item) {
    return item.name;
  }
  
  getUserInfoArray() {
    this.userInfo = [
      { name: 'Jared' },
      { name: 'Tom' },
      { name: 'Max' },
      { name: 'Wendy' },
      { name: 'Mars' },
      { name: 'Suns' },
    ];
  }
}

可以看到我们在 ngFor 的内容中,加入 trackBy: trackByItems 代表它会去追踪 trackByItems 这个函式回传的内容。在 trackByItems 函式有两个参数分别是 index 和 item ,index 就代表该元素在其所属阵列中的元素位置, item 的话就代表遍历当下的元素。然後,我们会回传 item.name 就是代表去鉴察是否该元素的 name 属性内容是之前阵列没有的。

以上这个新增的程序码效果,为若该元素的 name 属性内容没有变化的话,该元素的 template 是不会被重新渲染的喔。
让我们来看一下修改後的操作结果

可以发现并不是所有 ul 里面的 li 都被重新渲染,只有那些跟原本阵列元素内容不同的部分会被重新渲染,是不是很赞呢~~

Summary

来做个总结吧

  1. 我们可以透过 ngFor 来遍历某个阵列,而被遍历元素的同时,会为哀元素产生出属於它的样板。
  2. 可以加入 trackBy 属性来优化 ngFor 渲染机制。

<<:  Day04 把捷径真的变“捷径”吧

>>:  Day5 开始Vue前要具备的基础

Day44. 范例:文字积木 (蝇量模式)

本文同步更新於blog 情境:这是公司生产的文字积木 <?php namespace Ap...

DAY29-JAVA的for-each、Iterator和ListIterator

集合的特性 可以依照集合是否具有「自动排序性」、「重复性」、「次序性」及「使用关键值」,为资料选择适...

Day 10 : 用於生产的机械学习 - Data Define 与建立基准

接续介绍 ML 专案生命周期,本日说明第 2 阶段「资料 Data」的工作流程,依其说法分为2大步...

Day10 OLED进化成Micro OLED後来找Micro LED报仇了!?

上一期说到Micro LED的厉害之处,不过今天要说的是Micro OLED,Micro OLED继...

[第二十六天]从0开始的UnityAR手机游戏开发-输出64位元的APP

点开Project Settings的other,把Scripting Backend改为IL2CP...