Reputation: 1160
I started a new Angular 7 project in which I need to call an API to get a list of results. Then if a specific result is in the list I select it, if not I create it calling another API endpoint. All of this stuff should be done into the ngOnInit()
method.
To simplify the problem, I have the following code:
const subject = new BehaviorSubject(null);
subject.next(6);
const res$ = subject.pipe(
mergeMap(() => getValues()),
tap((res) => {
const exists = res.find(x => subject.value === x) || null;
if (exists === null) {
// TODO: call the getNew() function, but I don't want nested call
} else {
// Do nothing
}
})
);
// res$ should contains [1, 2, 3, 4, 5, 6]
res$.subscribe(x => console.log(x));
function getValues() {
// Mocking the API call
return of([1, 2, 3, 4, 5]);
}
function getNew() {
// Mocking the API call
return of(6);
}
At the end, the res$
observable should contains all the array (with the added value) and I want to avoid nested subscription.
Thanks a lot!
Upvotes: 2
Views: 274
Reputation: 20654
Two things:
Don't count on having access to the subject's value. Instead you can you the alternative form of mergeMap
which allows you to create a select the value from the source and new stream. In this case I use it to return a new item down the stream with both the source and value from the inner observable.
You can use just another mergeMap
to return either the original result or the result of the new api call by wrapping the orignal result in of
to create a new inner observable.
const res$ = subject.pipe(
mergeMap(x => getValues(), (orig, res) => ({ orig, res })),
mergeMap(({ orig, res }) => {
const exists = res.find(x => x === orig) || null;
return (exists)
? of(res)
: getNew().pipe(map(x => [... res, x]))
})
);
Upvotes: 2
Reputation: 2920
One way of solving this could be to split this up into two observables, and then combine them againg with combineLatest. We will use the resFirst$ observable twice, and filter it based on if it is null or not.
import { combineLatest } from 'rxjs';
---
const subject = new BehaviorSubject(null);
subject.next(6);
const resFirst$ = subject.pipe(
mergeMap(() => getValues()),
map((res) => res.find(x => subject.value === x) || null)
);
const resSecond$ = resFirst$.pipe(
filter(exists => exists === null), <--- FILTERING FOR IS NULL
mergeMap(() => getNew()) <--- SWITCHMAP COULD ALSO BE USED HERE DEPENDING ON WHAT YOU WANT
);
const final$ = combineLatest(resFirst$, resSecond$)
.pipe(filter(exists => exists !== null)) <--- FILTERING FOR NOT NULL
.subscribe(x => console.log(x));
function getValues() {
// Mocking the API call
return of([1, 2, 3, 4, 5]);
}
function getNew() {
// Mocking the API call
return of(6);
}
Upvotes: 0