I have a component with 2 inputs (or mor) and I want to:
- Trigger a method X the first time, when both value are set and exists
- Trigger a method X each time, if either one of the both value changes
<some-cmp [itemid]="activeItemId$ | async" [userId]="activeUserId$ | async"></some-cmp>
Both values can change at any time, so I figured using rxjs to build a stream lets me control everything. My current solution seems a bit hacky and is difficult to test. I use 2 BehaviourSubjects and combineLatest with a 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(...)
}
So my question are
- Is there a more elegant way to achieve this behavior?
- Is there a way to avoid the
debounceTime, which makes testing difficult?
The debounceTime is used in case both value do arrive at the same time and I don't want combineLatest to trigger the method twice.
You are right in using
combineLatest, it will only emit the first time after each source has emitted once and then will emit any time any source emits.Maybe
debounceTimeisn't necessary due to the initial behavior ofcombineLatest; it will not emit the first time until all sources emit. However if you typically receive subsequent emissions from both sources that occur within a short timeframe, use ofdebounceTimemay be an appropriate optimization.I think your code is fine. However, it may not be necessary to use
BehaviorSubjectsince you aren't really using the default value. You could use plainSubjectorReplaySubject(1).You could assign the result of your
combineLatestto another variable and subscribe to that insidengOnInitor use theasyncpipe in the template: