Je couronne plusieurs requêtes sur Firebase Fireestore. Ils (peuvent) renvoyer une observable. Je voudrais combiner toutes les observables en une observable.
J'ai essayé différents opérateurs RXJS, comme; CombinaTest, Forkjoin, Concat, Zip, Fusionner et Mergemap. Mais je n'ai pas été en mesure d'atteindre le résultat souhaité. P> dans la pièce ci-dessus, je reçois tous les livres privés d'auteurs [0]. Quand je reviendrai Concat (... Bookrefs) ou Concat (Bookrefs [0], Bookrefs [1]), je reçois toujours uniquement les livres d'auteurs [0]. Je m'attends à obtenir tous les livres de tous les auteurs fournis. P> P>
3 Réponses :
Je pense que la meilleure solution est d'utiliser fork join .
Fork
vous permet d'exécuter l'appel en parallèle. Le résultat de chaque appel est placé dans un objet ( join
) et vous pouvez obtenir le résultat de tous les appels.
Merci pour votre réponse! Quand je retourne forkJoin (bookRefs) à la fin de la fonction, j'obtiens une erreur de type. forkJoin renvoie un Observable
Comme @Doflamingo l'a dit, vous pouvez utiliser forkJoin pour les appeler en parallèle et recevoir une réponse avec un tableau de toutes les réponses résolues. Le problème que vous avez est que chaque réponse est un Book [], donc dans le forkJoin vous recevez un tableau de Book [] (Book [] []). Vous devez aplatir le livre [] [] dans un livre []. Vous pouvez le faire en utilisant une map et une fonction de réduction .
Ici, j'ai posté un code simple extrait de la console qui enregistre la réponse de forkJoin et le résultat après avoir appliqué la fonction de réduction. P >
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.5.2/rxjs.umd.js"></script>
function mockBooks$(key): Observable<Book[]> { return rxjs.of([key + '-book1', key + '-book2', key + '-book3']); } function getPrivateBooksFromAuthors(authors): Observable<Book[]> { let bookRefs: Observable<Book[]>[] = authors.map(key => mockBooks$(key)); return rxjs.forkJoin(bookRefs).pipe( rxjs.operators.tap((books) => console.log('After forkJoin', books)), // You need this flattening operation!! rxjs.operators.map(books => books.reduce((acc, cur) => [...acc, ...cur], []) )); } const authors = ['a1', 'a2', 'a3', 'a4', 'a5']; getPrivateBooksFromAuthors(authors).subscribe((data) => console.log('Final result', data));
J'espère que cela vous aidera!
Llorenç, merci pour votre réponse !! L'exemple de code a beaucoup aidé! Quand j'ai exécuté votre code, cela a très bien fonctionné. Dès que j'ai remplacé les mockBooks par mon véritable appel à ma collection Firestore, cela a échoué. Il m'a fallu quelques jours de plus pour le découvrir. Mais je pense que cela échoue si certains auteurs n'ont pas de livres dans la base de données. Remplacement de la fourche Mais j'avais vraiment besoin de votre carte et réduisez le code !! Merci!
Je suis content que cela ait aidé! forkJoin ne fonctionne peut-être pas pour vous car votre observable .valueChanges () peut ne jamais se terminer. Pour terminer, vous pouvez retourner: .valueChanges.pipe (take (1)). Ou utilisez CombineLatest comme: combineLatest (bookRefs) .pipe (take (1)). Remarque: si vous ne mettez pas le take (1), vous devrez vous désinscrire plus tard. Si vous souhaitez rester abonné, vous pouvez simplement supprimer la prise (1) et l'écoute pour chaque modification.
@Doflamingo nous a pointé dans la direction de forkJoin et @ Llorenç a donné un excellent exemple de code avec la carte et réduire les tuyaux pour combiner les observables en un seul observable. Pour moi, le forkJoin a échoué si certains auteurs n'avaient pas de livres. J'ai donc fini par utiliser combineLatest (qui n'attend pas que tous les Observables soient terminés). Ceci est mon code final (tout le mérite revient à @ Llorenç):
mockBooks$(key): Observable<Book[]> { return this.afs.collection<Book>('books', ref => ref.where('private', '==', true) .where('authors', 'array-contains', key) ).valueChanges() // return of([key + '-book1', key + '-book2', key + '-book3']); } getPrivateBooksFromAuthors(authors): Observable<Book[]> { let bookRefs: Observable<Book[]>[] = authors.map(key => this.mockBooks$(key)); // return combineLatest(bookRefs).pipe( // tap((books) => console.log('After forkJoin', books)), // // You need this flattening operation!! // map(books => books.reduce((acc, cur) => [...acc, ...cur], []) )); return combineLatest<Book[]>(bookRefs).pipe( map(arr => arr.reduce((acc, cur) => acc.concat(cur) ) ), ) }
J'ai commenté le code de Llorenç, pour montrer la différence. Merci à vous deux!