J'ai deux collections: les personnes et les animaux. Chaque animal a personId. Mon objectif est d'obtenir toutes les personnes et pour chacune d'elles d'ajouter ses animaux de compagnie en un seul json. Ce que j'ai fait jusqu'à présent, c'est:
this.personService.getPersions().subscribe(persons => { const personsWithPets = persons.flatMap(person => this.petService.getPetsByPersonId(person._id) .subscribe(petsData => { person.pets = petsData; return person; }, (err) => { console.log(err); })); this.persons = personsWithPets; // This is fired before previous subscribe }, (err) => { console.log(err); });
Qu'est-ce que je fais de mal? Pourquoi this.persons = personsWithPets;
est déclenché avant la fin de l'abonnement?
4 Réponses :
L'utilisation de subscribe dans subscribe est considérée comme une mauvaise pratique et peut entraîner des problèmes comme vous l'avez décrit. Je suggérerais d'utiliser mergeMap combiné avec l'opérateur forkJoin:
this.personService.getPersions().pipe(mergeMap(persons => { const requests = persons.map(person => this.petService.getPetsByPersonId(person._id)); return forkJoin(of(persons), ...requests); }), map(values => { const persons = values[0]; const pets = values.slice(1); // here you need to assign correct pet to correct person }) ).subscribe(personsWithPets => { console.log(personsWithPets); }, err => { console.log(err); });
Cette carte (valeurs => {me renvoie un tableau approprié d'animaux de compagnie, mais comment combiner personne et valeurs [nth]?
J'ai fait un exemple pour vous
<script src="https://unpkg.com/rxjs/bundles/rxjs.umd.min.js"></script>
const { of } = rxjs; const { map, switchMap, toArray, mergeMap } = rxjs.operators; function getPeople() { return of([{ id: 1, name: 'Amanda' }, { id: 2, name: 'Nancy' }]); } function getPetsByPersonId(id) { switch(id) { case 1: return of(['Doggie']); case 2: return of(['Kitten']); } } const getPets = (person) => { return getPetsByPersonId(person.id).pipe( map(pets => ({ ...person, pets })) ) } getPeople().pipe( switchMap(people => people), mergeMap(getPets), toArray() ) .subscribe(peopleWithPets => console.log(peopleWithPets));
le tuyau n’existait pas sur le type Abonnement
Quelle pipe? getPetsByPersonId ou getPeople? Pourriez-vous s'il vous plaît poster le code de ces fonctions?
maintenant j'ai TypeError: this.getPetsByPersonId n'est pas une fonction
Les observables sont asynchrones par nature. L'appel d'abonnement sera exécuté dans un autre thread tandis que le reste des instructions de la méthode continuera à s'exécuter dans le thread principal. Ce que vous devez faire est d'attendre la fin de l'action asynchrone avant d'exécuter la suivante. Vous pouvez le faire en mettant les isntructions à l'intérieur de l'abonnement, ou mieux encore utiliser le paramètre onCompleted qui vient après le paramètre (err), comme ceci
this.personService.getPersons().subscribe(persons => { const personsWithPets = persons.flatMap(person => this.petService.getPetsByPersonId(person._id) .subscribe(petsData => { person.pets = petsData; return person; }, (err) => { console.log(err); }, () => { this.persons = personsWithPets; })) });
Commentaires mis à jour ajoutés
Un autre: stackblitz
this.service.getPersons().pipe(switchMap((per:any[])=>{ //create an array of observables const obs=per.map(per=>this.service.getPet(per.id)); //call all of them in forkjoin return forkJoin(obs).pipe(map(pets=> //pets is an array, in pets[0] is the response of getPet(1), //in pets[1] is the response of getPet(2) pets.map((pet,i)=>{ return { ...per[i], //all the properties of the person pets:pet //+ in pets an array with the pets of the person } }) )) })).subscribe(res=>this.res=res)
car votre
getPetsByPersonId
est probablement asynchrone, donc le code à l'intérieur de votre appel àgetPetsByPersonId (). subscribe
ne retournera probablement pas de valeur avantthis.persons = personsWithPets < / code>. Le
subscribe
est appelé, seul le code à l'intérieur ne fonctionnera pas tant que le service n'aura pas renvoyé une valeur. Cela n'a vraiment rien à voir avec les observables imbriquésDes suggestions pour que cela fonctionne?