新新新手阅读 Angular 文件 - Component - Day23

本文内容

本文是阅读有关 Angular 的元件生命周期的 OnChanges 的笔记内容。

ngOnChanges

呼叫时机: 当元件中的 @Input 类型的属性,发生变化的时候,这个 lifecycle hook 就会被呼叫。
例如:
[TypeScript]

import { OnChanges, SimpleChanges, Input} from '@angular/core';

export class AppComponent implements OnChanges {
  @Input() book;

  ngOnChanges(changes: SimpleChanges) {
    console.log(this.book);
  }
}

上面的范例中,可以看到在这个元件里面定义了一个 @Input 的属性 book,每当 book 的内容有变化的时候,就会触发到 ngOnChanges 的 hook 喔。
ngOnChanges 会自带一个参数,他是一个物件,里面会包含的内容为 @Input 属性的现在的值和上一次的值。

传址的问题

这边我们先来写个 @InputngOnChanges 的范例
[子元件 - TypeScript]

@Component({
 selector: 'bcomp',
 template: `
   <div *ngFor="let user of users">
     {{ user }}
   </div>
 `
})
export class BComponent implements OnChanges {
 @Input() users
 ngOnChanges() {
   console.log("changed")
 }
}

[父元件 - TypeScript]

@Component({
 template: `
   <bcomp [users]="users"></bcomp>
 `
})
export class App {
 users = ["Jasper", "Tom", "Mary"]
}

可以看到在子元件定义了一个 @Input property 叫做 users,并把这个属性绑到父元件的里面,去接收来自父元件的 users 内容。
上面的内容,会呈现如下的画面
https://ithelp.ithome.com.tw/upload/images/20210923/20140093etcfBcT7U4.png

就很单纯的把资料渲染到画面上。

那如果今天当点击某个按钮,会逐一删除 users 里面的内容,那承如上面所讲过的当 @Input 属性的内容改变的时候,照道理讲子元件的 ngOnChanges 这个 hook 会被触发。
那来改写一下上面的范例吧
[子元件 - TypeScript]

export class BComponent implements OnInit, OnChanges {
  @Input() users;
  
  ngOnChanges(obj: SimpleChanges) {
    console.log(obj);
  }
}

[父元件 - TypeScript]

@Component({
  template: `
    <app-b [users]="users"></app-b>
    <button (click)="delete()">delete</button>
  `,
})
export class AppComponent {
  users = ['Jasper', 'Tom', 'Mary'];

  delete() {
    this.users.splice(0, 1);
  }
}

可以看到上面的范例,在父元件有个 delete 的函式,每当点击按钮就会触发它,并删除 users 的阵列的内容。
操作画面如下

可以看到画面上的阵列内容,确实地逐一被删除,但是, console.log 中并没有触发子元件的 ngOnChanges 里面的内容,将改变的内容印出来。
原因是什麽呢?
因为,users 阵列透过 splice 来删除它的阵列内容,但并没有改变它阵列的位址,所以,对 @Input 属性 users 来说,它都是指向同一个位址的阵列,尽管它内容有被删减,对 @Input 属性 users 来讲还是同一个人根本没变化,所以,才没有触发它的 ngOnChanges 内容罗。

那要怎麽改呢?
很简单,我们就用那种会回传新阵列的删除方法,也就是 slice
那我们来改写一下,父元件的内容
[父元件 - TypeScript]

@Component({
  template: `
    <app-b [users]="users"></app-b>
    <button (click)="delete()">delete</button>
  `,
})
export class AppComponent {
  users = ['Jasper', 'Tom', 'Mary'];

  delete() {
    this.users = this.users.slice(1);
  }
}

你可以看到,我们把经过 slice 删除後产生的阵列回传给原本的 users,让它去指向另一个位址的新阵列。
操作结果如下

可以看到子元件的 ngOnChanges 函式就会被触发罗,而且我们可以透过印出 ngOnChanges 参数内容,去看每一次子元件的 users 阵列内容是真的也被删除掉了。

Summary

来做个总结

  1. ngOnChanges 会在元件的 @Input 属性有改变的时候,被触发。
  2. 要注意在 @Input 内容的传址问题,会导致子元件的 ngOnChanges 不会被触发,若要改变这种现象的话,就必须使用 non-mutating method (也就是会回传新的物件的方式) 来达成。

Reference

  1. Introduce ngOnChanges
  2. Angular OnChanges officail doc
  3. Non-mutating methods intro

<<:  day8: CSS style 规划 - CSS in JS(emotion 使用 - 2)

>>:  Day8 资源指派与沟通管理

[ JS个人笔记 ] 资料型别—DAY1

资料型别 为何需判断型别,其因为电脑在执行时,需先判断资料是何种型别,才可采取运算方式。例如数字12...

Episode 1 - 真.即将失传的古老技艺

如果画面太小或看不清楚,可移驾至 https://www.youtube.com/watch?v=...

【Day10-去重】使用python优雅的一行解决list或DataFrame资料去重问题

前一天,我们简单讨论了一下面对缺失值资料的处理 那今天就反过来讨论一下面对资料中有重复的情况应该要怎...

Day.15 「条件设定好~让程序判断!」 —— JavaScript 条件判断式

前面学习了基础的变数与运算子的使用,这些基础往往是非常枯燥乏味的,还没有与电脑有更进一步的互动,所...

# Day23--从广场到仓库,原来add跟commit是这样!

在上一篇开始进到终端机的操作後,我们接着要来把一些东西真正让git来进行版本控制。 在这个章节,主要...