【问题标题】:RxJs - get defer() observable value when other emitsRxJs - 在其他发射时获取 defer() 可观察值
【发布时间】:2021-01-27 12:26:02
【问题描述】:

这实际上是针对 Angular 应用的。

我有一个过滤表单字段,只要用户更改其值(例如打字),它就会发出值。这是第一个 Observable。

this.filterFrm.valueChanges.pipe(...);

我还有另一个 Observable,它只给了我当前的表单有效性值:

const formIdValid = defer(() => of(this.filterFrm.valid));

如何将这两者结合起来,以便在表单值更改时始终获得当前的form.valid 值?

这是我尝试过的:

    this.filterFrm.valueChanges
      .pipe(
        concatMap((frmValue) => formIdValid.pipe(map((isValid) => ({ isValid, frmValue })))),
        filter(({ isValid, frmValue }) => isValid),
        takeUntil(this.destroy)
      )
      .subscribe((filterCriteria) => this.filterChanged.emit(filterCriteria));

但由于某种原因它只发出单个值。

【问题讨论】:

    标签: angular rxjs


    【解决方案1】:

    在我发布问题后,我就成功了!

        this.filterFrm.valueChanges
          .pipe(
            debounceTime(400),
            distinctUntilChanged(),
            concatMap((frmValue) => formIdValid.pipe(map((isValid) => ({ isValid, frmValue })))),
            tap(console.dir),
            filter(({ isValid, frmValue }) => isValid),
            map(({ isValid, frmValue }) => ({
              searchPhrase: frmValue.filterFld,
              startDate: frmValue.startDateFld ? new Date(frmValue.startDateFld) : undefined,
              endDate: frmValue.endDateFld ? new Date(frmValue.endDateFld) : undefined
            })),
            takeUntil(this.destroy)
          )
          .subscribe((filterCriteria) => this.filterChanged.emit(filterCriteria));
    

    更新

    是的,在这种特殊情况下,defer() 不是必需的。但无论如何,这是一个很好的学习实验。

        this.filterFrm.valueChanges
          .pipe(
            debounceTime(400),
            distinctUntilChanged(),
            filter(() => this.filterFrm.valid),
            map((frmValue) => ({
              searchPhrase: frmValue.filterFld,
              startDate: frmValue.startDateFld ? new Date(frmValue.startDateFld) : undefined,
              endDate: frmValue.endDateFld ? new Date(frmValue.endDateFld) : undefined
            })),
            takeUntil(this.destroy)
          )
          .subscribe((filterCriteria) => this.filterChanged.emit(filterCriteria));
    

    【讨论】:

    • 解构时,您不需要分配对象的所有属性。 filter(({ isValid }) => isValid)map(({ frmValue }) => ({... 更清楚一点。
    • 好吧,我所有的项目都使用--strict (tsconfig.json),所以有一个非常明确的代码是一种习惯。
    【解决方案2】:

    我真的想不出你想要这样的 observable 的任何情况:

    const formIdValid = defer(() => of(this.filterFrm.valid));

    它基本上只是使用一点额外的计算能力来阅读this.filterFrm.valid,无论您订阅哪里。

    例如,您的解决方案可以像这样重写,没有任何问题:

    this.filterFrm.valueChanges.pipe(
      debounceTime(400),
      distinctUntilChanged(),
      map(frmValue => ({
        frmValue, 
        isValid: this.filterFrm.valid,
      })),
      tap(console.dir),
      filter(({ isValid }) => isValid),
      map(({ frmValue }) => ({
        searchPhrase: frmValue.filterFld,
        startDate: frmValue.startDateFld ? new Date(frmValue.startDateFld) : undefined,
        endDate: frmValue.endDateFld ? new Date(frmValue.endDateFld) : undefined
      })),
      takeUntil(this.destroy)
    ).subscribe(
      filterCriteria => this.filterChanged.emit(filterCriteria)
    );
    

    或者如果你不关心你的console.dir日志是有效的,那么这也是一样的:

    this.filterFrm.valueChanges.pipe(
      debounceTime(400),
      distinctUntilChanged(),
      tap(console.dir),
      filter(_ => this.filterFrm.valid),
      map(frmValue => ({
        searchPhrase: frmValue.filterFld,
        startDate: frmValue.startDateFld ? new Date(frmValue.startDateFld) : undefined,
        endDate: frmValue.endDateFld ? new Date(frmValue.endDateFld) : undefined
      })),
      takeUntil(this.destroy)
    ).subscribe(
      filterCriteria => this.filterChanged.emit(filterCriteria)
    );
    

    旁白:defer 到底在做什么?

    考虑以下几点:

    let num = 10;
    
    const $1 = of(num);
    const $2 = defer(()=>of(num));
    const $3 = of(1).pipe(map(_ => num));
    const $4 = of(1).pipe(mapTo(num));
    
    num++;
    
    $1.subscribe(console.log);
    $2.subscribe(console.log);
    $3.subscribe(console.log);
    $4.subscribe(console.log);
    

    如果你运行这个,你期望输出什么?要正确回答这个问题,您需要了解的是 num 的值何时被解析。对于$1$4,这是在创建可观察对象时完成的,这意味着您将获得num 的第一个值。对于$2$3num 在调用defer/map 的 lambda 时被解析。

    • defer 在订阅之前不会调用其工厂函数。
    • map 每次有新值到达时都会调用它的转换函数,最早是在订阅之后的一段时间。

    因此,defermap 最终都访问了 num 的第二个值

    输出

    10
    11
    11
    10
    

    返回this.filterFrm.valid

    问题来自 this.filterFrm.valid 解决后。一个函数(在本例中为 filter 的 lambda)在调用之前不会访问该变量。过滤器只有在获得新值时才会调用该函数,因此对于每个新值,过滤器都会重新访问this.filterFrm.valid

    所以在这种情况下,defer 没有任何好处

    【讨论】:

    • hm...我使用defer() 的原因是与您的解决方案类似的解决方案不起作用。但我明天会再试一次,因为它真的很有趣。
    • @alvipeo 我添加了一个简短的简介来描述这应该何时/如何工作。
    • 好的,我试过了,你是对的。它只适用于filter()。但我确实尝试过这个......这可能是让它失败的其他原因。该页面非常复杂。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-01
    • 1970-01-01
    • 2020-12-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多