Angular中如何通过Signal Input检测firstChange
用Angular Signal Input + Effect 实现输入属性首次变更检测
在旧版Angular中,我们通常通过ngOnChanges钩子区分输入属性的首次变更和后续变更,代码示例如下:
@Input() foo = ''; ngOnChanges(changes: Record<string, SimpleChange | undefined>): void { if (changes['foo']) { if (changes['foo'].firstChange) { // 首次变更处理逻辑 console.log('first change', changes['foo'].currentValue); } else { // 后续变更处理逻辑 console.log( '2+ changes', changes['foo'].previousValue, changes['foo'].currentValue ); } } }
而在基于Signal的Angular组件中,官方明确:
仅保留
ngOnInit和ngOnDestroy两个生命周期方法ngOnChanges的功能由Signal配合computed(派生值)或effect(处理副作用)替代
如果尝试将Signal转为Observable并使用pairwise,会发现pairwise不会触发首次值的回调。下面给出两种基于Signal+Effect的实现方案:
方案一:用本地状态追踪首次变更与前值
通过本地变量标记是否为首次变更,再用一个Signal存储前值,结合Effect监听输入Signal的变化:
import { Component, input, effect, signal } from '@angular/core'; @Component({ selector: 'app-cmp', standalone: true, template: `{{ foo() }}`, }) export class CmpComponent { // 定义Signal输入 foo = input(''); // 存储输入的前值 private previousFoo = signal(this.foo()); // 标记是否为首次变更 private isFirstChange = true; constructor() { effect(() => { const currentVal = this.foo(); if (this.isFirstChange) { // 首次变更逻辑 console.log('first change', currentVal); this.isFirstChange = false; } else { // 后续变更逻辑,可获取前值 console.log('2+ changes', this.previousFoo(), currentVal); } // 更新前值,需开启allowSignalWrites允许在Effect中修改Signal this.previousFoo.set(currentVal); }, { allowSignalWrites: true }); } }
方案二:Observable结合startWith补充首次值
如果偏好Observable的写法,可以通过toObservable将Signal转为Observable,再用startWith补充首次值,配合pairwise获取前后值:
import { Component, input, OnDestroy } from '@angular/core'; import { toObservable } from '@angular/core/rxjs-interop'; import { pairwise, startWith, takeUntil } from 'rxjs'; import { Subject } from 'rxjs'; @Component({ selector: 'app-cmp', standalone: true, template: `{{ foo() }}`, }) export class CmpComponent implements OnDestroy { foo = input(''); private destroy$ = new Subject<void>(); constructor() { const foo$ = toObservable(this.foo); foo$.pipe( // 补充初始值,确保首次变更能被触发 startWith(this.foo()), pairwise(), // 组件销毁时自动取消订阅 takeUntil(this.destroy$) ).subscribe(([prevVal, currVal]) => { // 判断是否为首次变更:初始值等于输入的默认值 if (prevVal === this.foo().initialValue) { console.log('first change', currVal); } else { console.log('2+ changes', prevVal, currVal); } }); } ngOnDestroy(): void { this.destroy$.next(); this.destroy$.complete(); } }
两种方案中,方案一更贴合Signal组件的设计理念,无需引入Rxjs操作符,逻辑更简洁。
内容的提问来源于stack exchange,提问作者Robert
相关产品推荐
相关产品推荐

