You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 11:47:24