设置 2 个输入后触发一个函数,然后如果 Angular 中的任何一个值发生变化则触发一个函数

Trigger a function once 2 inputs are set and then afterwards if either of the value changes in Angular

我有一个带有 2 个输入(或 mor)的组件,我想:

  1. 当两个值都已设置且存在时,第一次触发方法 X
  2. 如果两个值之一发生变化,则每次触发方法 X
<some-cmp [itemid]="activeItemId$ | async" [userId]="activeUserId$ | async"></some-cmp>

这两个值都可以随时更改,所以我认为使用 rxjs 构建流可以让我控制一切。我目前的解决方案似乎有点老套,而且很难测试。我使用 2 BehaviourSubjectscombineLatest 以及 debounceTime.

@Input() set itemId (id){this.itemId$.next(id)};
@Input() set userId (id){this.userId$.next(id)};

itemId$ = new BehaviourSubject$(null);
userId$ = new BehaviourSubbject$(null);

ngOnInt(){
    combineLatest([
        this.itemId$.pipe(filter(item=>item!===null)),
        this.userId$.pipe(filter(item=>item!===null))
    ]).pipe(
        debounceTime(10),
        switchMap(...)
    ).subscribe(...)
}

所以我的问题是

  1. 是否有更优雅的方法来实现此行为?
  2. 有没有办法避免debounceTime,这会使测试变得困难?

使用 debounceTime 以防两个值同时到达并且我不希望 combineLatest 触发该方法两次。

Angular 提供了 ngOnChanges 钩子,可以在这种情况下使用。只要组件的任何输入发生变化,它就会触发 ngOnChanges 方法。

下面是如何实现的示例:

export class SomeComponent implements OnChanges {
    @Input() itemId: any;
  
    @Input() userId: any;
  
    ngOnChanges(changes: SimpleChanges) {
      const change = changes.itemId || changes.userId;
  
      if (change && change.currentValue !== change.previousValue) {
        this.doSomething();
      }
    }
  
    private doSomething() {
      // Your logic goes here
    }
  }

您的 HTML 现在看起来很干净,您也可以摆脱 async

<some-cmp [itemid]="itemId" [userId]="userId"></some-cmp>

你使用combineLatest是正确的,它只会在每个源发射一次后第一次发射,然后在任何源发射时都会发射。

Is there a way to avoid the debounceTime. [It] is used in case both value do arrive at the same time and I don't want combineLatest to trigger the method twice.

由于 combineLatest 的初始行为,可能 debounceTime 不是必需的;在所有源发出之前,它不会第一次发出。但是,如果您通常在短时间内收到来自两个来源的后续排放,则使用 debounceTime 可能是一种适当的优化。

Is there a more elegant way to achieve this behavior?

我认为你的代码很好。但是,可能没有必要使用 BehaviorSubject,因为您并没有真正使用默认值。您可以使用普通 SubjectReplaySubject(1).

您可以将 combineLatest 的结果分配给另一个变量并在 ngOnInit 中订阅该变量,或者使用模板中的 async 管道:

@Input() set itemId (id){ this.itemId$.next(id) };
@Input() set userId (id){ this.userId$.next(id) };

itemId$ = new Subject<string>();
userId$ = new Subject<string>();

data$ = combineLatest([
    this.itemId$.pipe(filter(i => !!i)),
    this.userId$.pipe(filter(i => !!i))
]).pipe(
    debounceTime(10),
    switchMap(...)
);

ngOnInit() {
  this.data$.subscribe(...);
}