J'utilise un observable pour interroger ma base de données. Cette observable renverra un tableau avec tous les objets correspondants trouvés. Mon problème est que je veux mapper l'observable avec plus de détails que je récupérerai d'une autre API.
J'ai essayé concatMap mais il ne m'a permis d'imbriquer 1 observable dans l'observable initial
const usersAPI$: Observable<Users[]> = // something const assistsAPI$: Observable<Assists[]> = // something const modifiedAssists$ = assistsAPI$.find().pipe( concatMap(assists => assists.map(assist => usersAPI$.get(assist.userID) .pipe( map(user => assist = {...assist}, ...{userName: user.userName} ) ) ) ) );
Vous pouvez voir un exemple de travail similaire ici, sur stackblitz, https://stackblitz.com/edit/rxjs-concatmap-issue-stackoverflow où le "résultat" est la manière correcte d'utiliser concatMap et le "result2" est la manière dont je m'attendais à ne pas fonctionner
3 Réponses :
Vous pouvez utiliser combineLatest ()
(comme importation statique non pipe)
import {combineLatest} from 'rxjs'; const usersAPI$: Observable<Users[]> = // something const assistsAPI$: Observable<Assists[]> = // something const combinedObservable$ = combineLatest(usersAPI$,assistsAPI$, someOtherStuff$); const modifiedAssists$ = combinedObservable$.pipe( map([usersApiValues, assistsAPIValues, someOtherStuffValues] => { /** what you want here */ }) );
Il suffit de prendre en compte que chacun des Les observables dans combineLatest
doivent émettre au moins une fois (vous pouvez les démarrer avec un tableau vide par exemple)
J'ai besoin de savoir combien de assistsAPIValues
j'ai, avant de demander des usersApiValues
. Je dois transformer l'observable assistsAPI $
afin qu'il renvoie le même tableau du flux mais avec la propriété userName
déjà attribuée dans chaque assist
dans le tableau assists
renvoyé par assistsAPI $
Je crois que vous voulez mergeMap
?
Il mappe l'observable externe à l'observable interne résolue pour chaque émition externe.
const result = clicks.pipe( concatMap(ev => interval(1000).pipe(take(4)) ) ); const result2 = clicks.pipe( mergeMap(ev => arr.map(() => interval(1000).pipe(take(4))) ) ); result.subscribe(x => { console.log(x) }); result2.subscribe(x => { console.log(x) });
le mergeMap
(comme le concatMap
) ne résout pas les observables créés par le arr.map ()
intérieur comme prétendu.
Vous devez traiter votre tableau interne d'Observables d'une manière ou d'une autre. Vous pouvez utiliser forkJoin
, merge
ou concat
selon vos besoins.
forkJoin traitera vos appels d'API internes en parallèle et retournera un tableau lorsque tous les appels d'API seront terminés. Notez que les observables internes doivent être terminés. Je pense que c'est ce qui devrait vous convenir dans votre application.
import { concat } from 'rxjs'; const modifiedAssists$ = assistsAPI$.find().pipe( concatMap(assists => concat(...assists.map(assist => someObservable )), // toArray() add the toArray operator if you need the result as an array );
merge
s'abonnera à tous les appels d'API internes à la fois et émettre les résultats de tous les appels un par un à mesure qu'ils arrivent.
import { merge } from 'rxjs'; const modifiedAssists$ = assistsAPI$.find().pipe( concatMap(assists => merge(...assists.map(assist => someObservable )), // toArray() add the toArray operator if you need the result as an array );
Cela vous donnera le résultat souhaité dans votre stackblitz, mais je pense que votre stackblitz est quelque peu trompeur et pas ce que vous recherchez dans l'exemple de code de votre question, car l'observable interne de votre stackblitz émet plusieurs fois et la sortie finale n'est pas un tableau. Si l'ordre des requêtes internes n'a pas d'importance, elles émettent toutes une valeur puis se terminent et vous avez besoin des résultats de toutes les requêtes sous forme de tableau, utilisez simplement forkJoin
.
concat
s'abonnera à tous les appels d'API internes l'un après l'autre. Le prochain Observable de la séquence ne sera abonné qu'une fois le précédent terminé. L'exécution sera donc plus lente qu'avec forkJoin
ou merge
car la prochaine requête HTTP ne sera exécutée qu'après que la précédente a renvoyé une valeur. Utilisez cette option si les appels à votre userAPI
doivent spécifiquement être effectués dans le même ordre que les assists
dans votre tableau.
import { forkJoin } from 'rxjs'; const modifiedAssists$ = assistsAPI$.find().pipe( concatMap(assists => forkJoin(assists.map(assist => someObservable )) );
Je vous remercie! Votre solution concat
fonctionnait plutôt bien pour le but que je voulais (plus avec le toArray ()
comme vous l'avez suggéré aussi)!
Vous pouvez utiliser
mergeMap
au lieu deconcatMap
.@martin le
mergeMap
me pose le même problème que leconcatMap
. (Et ce sont en fait des opérateurs très similaires. Je recherche une solution différente)Si
mergeMap
se comporte de la même manière, regardez ce queassistsAPI $ .find ()
car il n'émet probablement pas les éléments que vous attendez.Le backend est FeathersJS. Je suis certain de ce que l'API $ .find () émet. Ce n'est pas le but ici. Mais je suis reconnaissant que vous essayiez d'aider, merci :)