Angular4生命周期钩子ngOnChanges 补充监听子组件

3,832 阅读3分钟

简介

Angular 指令的生命周期,它是用来记录指令从创建、应用及销毁的过程。Angular 提供了一系列与指令生命周期相关的钩子,便于我们监控指令生命周期的变化,并执行相关的操作。Angular 中所有的钩子如下图所示:

image.png

生命周期介绍

  • 指令与组件共有的钩子
    1. ngOnChanges
    2. ngOnInit
    3. ngDoCheck
    4. ngOnDestroy
  • 组件特有的钩子
    1. ngAfterContentInit
    2. ngAfterContentChecked
    3. ngAfterViewInit
    4. ngAfterViewChecked

调用顺序

ngOnChanges - 当数据绑定输入属性的值发生变化时调用 ngOnInit - 在第一次 ngOnChanges 后调用 ngDoCheck - 自定义的方法,用于检测和处理值的改变 ngAfterContentInit - 在组件内容初始化之后调用 ngAfterContentChecked - 组件每次检查内容时调用 ngAfterViewInit - 组件相应的视图初始化之后调用 ngAfterViewChecked - 组件每次检查视图时调用 ngOnDestroy - 指令销毁前调用

ngOnChanges

  • 用处:当Angular设置数据绑定输入属性发生变化时响应。
  • 时机:当被绑定的输入属性的值发生变化时调用,不过首次调用是会发生在ngOnInit()之前的。 ngOnChanges()方法获取了一个对象,它可以同时观测1个/多个绑定的属性值,它把每个发生变化的属性名都映射到了一个SimpleChange对象, 该对象中有属性的当前值和前一个值。
  • 触发条件 @input属性(输入属性)发生变化时,会调用。非此属性,不会调用。 当输入属性为对象时,当对象的属性值发生变化时,不会调用,当对象的引用变化时会触发。 输入属性,使用@Input装饰的属性,这里还需要注意不可变对象,在Angular中,典型的不可变对象是string类型,但所有自定义对象均为可变对象,如:user:{name:string},可变对象即使被定义为输入属性,也不会触发OnChanges方法。
export interface SimpleChanges { [propName: string]: SimpleChange; }

SimpleChange的结构如下:

export declare class SimpleChange {
    previousValue: any;
    currentValue: any;
    firstChange: boolean;
    constructor(previousValue: any, currentValue: any, firstChange: boolean);
    /**
     * Check whether the new value is the first value assigned.
     */
    isFirstChange(): boolean;
}

假如我们的组件中有一个满足触发OnChanges钩子条件的属性名叫inputVal,那么可以通过如下方式获取它变化前后的值:

// Angular定义SimpleChanges类构造函数三个参数分别为上一个值,当前值和是否第一次变化(firstChange: boolean),这些changes都可以调用。
ngOnChanges(changes: SimpleChanges): void {
    console.log('ngOnChanges中inputVal变更前值为:'+ changes['inputVal'].previousValue);
    console.log('ngOnChanges中inputVal变更后值为:'+ changes['inputVal'].currentValue);
    console.log('ngOnChanges中inputVal是否是一次改变:'+ changes['inputVal'].firstChange);
  }

栗子

eventtest.ts.png

项目结构.jpg

父组件html.png

父组件ts.png

子组件html.jpg

子组件ts.jpg

====================补充分割线====================

关于在组件中监听某属性值的变化问题,在angularjs1.x中,可以使用$watch进行实时监听,但在ng2后取消了$watch指令,以至于当遇到所需场景是有一种手足无措的感觉。 在刚遇到该问题的时候,我采用了ngDoCheck(),多次的检查以及无数的无法预估问题让我放弃,进而调研ngOnChange(),发现貌似必须要在@Input的前提下使用(未确定,欢迎指正),不符合场景要求,依然无法满足需求。

随后在【segmentfault】提出问题,收获两个思路,个人比较推荐第二个思路,符合ng4的设计模式,故而分享出来:

  • 思路 1:
// 在service中声明该变量,并利用定时器不断调用
@Injectable()
class CustomService{
    public customAttr: number = 0;
    constructor() {
        setInterval(() => this.customAttr = Math.random(), 1000);
    }
}

constructor(public customService: CustomService) { }

<div>{{customService.customAttr}}</div>
  • 思路 2 :
// 引入 BehaviorSubject 模块,也可以直接使用Subject订阅模式,BehaviorSubject 更优在于每次只获取最后一次的值
import { BehaviorSubject } from 'rxjs/BehaviorSubject'
...
export CustomService {
    customAttr: BehaviorSubject<any[]> = new BehaviorSubject<[]>
    ...
}

// 每次修改下拉值只需(设注入service为CustomService ):
// CustomService .downData.next(更新的数据)
// 所有的子组件引用服务后只需在模版使用async pipe便可自动监听订阅数据变化:

<div *ngFor="let item of (CustomService .downData | async)"></div>


// 也可以直接在组件中订阅:
CustomService .downData.subscribe(data => {
    // 监听到变化
    ...
})