设置 2 个输入后触发一个函数,然后如果 Angular 中的任何一个值发生变化则触发一个函数
Trigger a function once 2 inputs are set and then afterwards if either of the value changes in Angular
我有一个带有 2 个输入(或 mor)的组件,我想:
- 当两个值都已设置且存在时,第一次触发方法 X
- 如果两个值之一发生变化,则每次触发方法 X
<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
是正确的,它只会在每个源发射一次后第一次发射,然后在任何源发射时都会发射。
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
,因为您并没有真正使用默认值。您可以使用普通 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(...);
}
我有一个带有 2 个输入(或 mor)的组件,我想:
- 当两个值都已设置且存在时,第一次触发方法 X
- 如果两个值之一发生变化,则每次触发方法 X
<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
是正确的,它只会在每个源发射一次后第一次发射,然后在任何源发射时都会发射。
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
,因为您并没有真正使用默认值。您可以使用普通 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(...);
}