【问题标题】:Trigger a function once 2 inputs are set and then afterwards if either of the value changes in Angular一旦设置了 2 个输入就触发一个函数,然后如果 Angular 中的任何一个值发生变化,则触发一个函数
【发布时间】:2021-07-25 03:28:56
【问题描述】:

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

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

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

@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 触发该方法两次。

【问题讨论】:

标签: javascript angular rxjs angular-input


【解决方案1】:

您使用combineLatest 是对的,它只会在每个源发出一次后第一次发出,然后在任何源发出的任何时候发出。

有没有办法避免去抖时间。 [It] 用于两个值同时到达并且我不希望 combineLatest 触发该方法两次的情况。

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

有没有更优雅的方式来实现这种行为?

我认为您的代码很好。但是,可能没有必要使用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(...);
}

【讨论】:

    【解决方案2】:

    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>
    

    【讨论】:

    • 谢谢,我知道 ngOnChanges,但它所做的只是替换设置器,因为它无法跟踪两个值都存在的状态。
    • @HanChe 其实ngOnChanges 很强大。它收到的更改遵循SimpleChange 接口 (angular.io/api/core/SimpleChange),该接口跟踪每个输入道具的所有重要信息,如 prrevValue、currentValue、isFirstChange 等。因此,它绝对适合 IMO 这个问题。
    猜你喜欢
    • 2022-08-04
    • 1970-01-01
    • 1970-01-01
    • 2012-06-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-06-03
    相关资源
    最近更新 更多