问题描述
<some-cmp [itemid]="activeItemId$ | async" [userId]="activeUserId$ | async"></some-cmp>
这两个值都可以随时更改,因此我认为使用 rxjs
构建流可以让我控制一切。我目前的解决方案似乎有点老套,难以测试。我将 2 BehavIoUrSubjects
和 combineLatest
与 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(...)
}
所以我的问题是
- 是否有更优雅的方式来实现这种行为?
- 有没有办法避免使测试变得困难的
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
是正确的,它只会在每个源发出一次后第一次发出,然后在任何源发出时都会发出。
有没有办法避免 debounceTime。 [It] 用于两个值同时到达并且我不希望 combineLatest 触发该方法两次的情况。
由于 debounceTime
的初始行为,可能不需要 combineLatest
;它不会第一次发射,直到所有源发射。但是,如果您通常会在短时间内收到来自两个来源的后续排放,则使用 debounceTime
可能是适当的优化。
有没有更优雅的方式来实现这种行为?
我认为你的代码很好。但是,可能没有必要使用 BehaviorSubject
,因为您并没有真正使用默认值。您可以使用普通的 Subject
或 ReplaySubject(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(...);
}