alvarosaburido
alvarosaburido

Reputation: 43

Combine two rxjs streams and emit only if the first one it's true

I will need some help with a use case I'm stuck right now:

I'm implementing an autosaving feature in Angular 6 and Ngrx for a dialog 'form' (this case doesn't use but it changes the state with some actions when clicking) with the following conditions:

Currently, for the first case I have this observable:

this.configuration$ = this.store.pipe(select(getTopicsList));
this.configuration$
    .pipe(
    distinctUntilChanged(),
    debounceTime(30000),
    skip(1),
).subscribe(res => this.saveCurrent());

Note: the skip(1) is only to skip the first emit when the component is being initialized

For the second I have and Subject converted to an Observable to emit an event every time the dialog is closed:

this.close$ = this.closeStream.asObservable();

With Observable operators, there is any way to combine both and condition the emit if changes in the this.configuration$observable had been made?

I'm not that strong in observables but maybe I'm overthinking and there is an efficient solution. It will be awesome in someone can give me a head with it. Thank you very much.

Upvotes: 4

Views: 2985

Answers (2)

Danziger
Danziger

Reputation: 21161

You could use race but only after the form has changed.

Basically, every time the form changes, you want to save it either after a certain delay or once the user closes the modal, whatever comes first.

Like this, if instead of saving the form when the modal is closed you want to add a Save button, it would only be saved once until it changes again.

It should be something like this:

const {
  Subject,
  of,
  race
} = rxjs;

const {
  distinctUntilChanged,
  skip,
  switchMap,
  delay,
  take
} = rxjs.operators;

let field = 0;

const formSubject = new Subject();
const closeSubject = new Subject();
const formObservable = formSubject.asObservable();
const closeObservable = closeSubject.asObservable();

document.getElementById('change').onclick = () => {
  console.clear();
  
  formSubject.next({ field: ++field });
};

document.getElementById('close').onclick = () => {
  console.clear();
  
  closeSubject.next(true);
};

formObservable.pipe(
  skip(1),
  distinctUntilChanged(),
  switchMap((form) => race(
    of(form).pipe(delay(2000)),
    closeObservable.pipe(take(1)).pipe(switchMap(() => of(form)))
  ))
).subscribe(res => {
  console.log(res);
});

formSubject.next({ field });
<div>
  <button id="change" onclick="change()">CHANGE</button>
  <button id="close" onclick="close()">CLOSE</button>
</div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.2.2/rxjs.umd.js"></script>

Upvotes: 2

JoshSommer
JoshSommer

Reputation: 2618

You want to use race. for example race(observableOne$, observableTwo$) will only emit the value of the first Observable to emit a value.

https://www.learnrxjs.io/operators/combination/race.html

Upvotes: 0

Related Questions