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
debounceTime
isn'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 ofdebounceTime
may be an appropriate optimization.I think your code is fine. However, it may not be necessary to use
BehaviorSubject
since you aren't really using the default value. You could use plainSubject
orReplaySubject(1)
.You could assign the result of your
combineLatest
to another variable and subscribe to that insidengOnInit
or use theasync
pipe in the template: