Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
228 views
in Technique[技术] by (71.8m points)

angular - RxJs - get defer() observable value when other emits

This is actually for angular app.

I have a filter form fields which emit values whenever user changes their values (typing, for example). This is the first Observable.

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

And I have another Observable which just gives me the current form validity value:

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

How can I combine these two so when form values change I always get the current form.valid value?

Here's what I tried:

    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));

But it only emits single value for some reason.

question from:https://stackoverflow.com/questions/65919139/rxjs-get-defer-observable-value-when-other-emits

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

I cant really think of any situations in which you'd want an observable like this:

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

It's basically just using a bit of extra computing power to read this.filterFrm.valid wherever you subscribe.

For example, your solution can be re-written like this without any issue:

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)
);

Or if you don't care about your console.dir logging isValid, then this is also the same:

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)
);

Aside: What is defer really doing?

consider the following:

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);

If you run this, what do you expect as output? What you need to understand to answer this properly is when the value of num is resolved. For both, $1 and $4, this is done when the observable is created, which means that you get the first value of num. For $2 and $3, num is resolved when the lambda given to defer/map is invoked.

  • defer doesn't invoke its factory function until subscription.
  • map invokes its transformation function each time a new value arrives, which is some time after subscription at the earliest.

Therefore, both defer and map end up accessing the second value of num

The Output

10
11
11
10

Back to this.filterFrm.valid

The problem comes from when this.filterFrm.valid is resolved. A function (in this case the lambda given to filter) doesn't access that variable until it is invoked. Filter only invokes the function when it gets a new value and so for every new value, the filter will access this.filterFrm.valid anew.

So in this case, you're not gaining anything with defer


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...