Tengo un servicio Angular que devuelve una matriz de objetos "cleaningDuty". Dentro de un objeto de servicio, hay un objeto anidado llamado "currentCleaner" con una identificación.
[ { // other cleaningDuty data currentCleaner: { id: string; active: boolean; }; }, { // other cleaningDuty data currentCleaner: { id: string; active: boolean; }; }, { // other cleaningDuty data currentCleaner: { id: string; active: boolean; }; } ] con la ayuda de currentCleaner.id , quiero obtener los datos de usuario de currentCleaner de un UserService dinámicamente en el mismo método pipe() y agregar los datos de usuario devueltos al objeto cleaningDuty. Entonces el objeto debería verse así:
{ // other cleaningDuty data currentCleaner: { id: string; active: boolean; }, cleanerData: { name: string; photoUrl: string; // other user data } }, Desafortunadamente, no puedo hacer que esto funcione incluso después de invertir días en ello. Probé casi todas las combinaciones de forkJoin() , mergeMap() y así sucesivamente. Sé que un método de subscribe() dentro del componente de destino haría el trabajo, pero quiero escribir el mejor código posible en cuanto a calidad. Este es mi estado actual del método de servicio (agrega el usuario observable en lugar del valor al objeto cleaningDuty):
getAllForRoommates(flatId: string, userId: string) { return this.firestore .collection('flats') .doc(flatId) .collection('cleaningDuties') .valueChanges() .pipe( mergeMap((duties) => { let currentCleaners = duties.map((duty) => this.userService.getPublicUserById(duty.currentCleaner.id), ); return forkJoin([currentCleaners]).pipe( map((users) => { console.log(users); duties.forEach((duty, i) => { console.log(duty); duty.cleanerInfos = users[i]; }); return duties; }), ); }), ); } El método getPublicUserById() :
getPublicUserById(id: string) { return this.firestore.collection('publicUsers').doc(id).valueChanges(); }¡Cualquier ayuda sería muy apreciada!
La función forkJoin :
Espere a que los Observables se completen y luego combine los últimos valores que emitieron; completar inmediatamente si se pasa una matriz vacía.
Entonces, debe asegurarse de que todos los Observables internos se hayan completado, luego forkJoin emitirá los valores combinados, y puede lograrlo usando el operador take (1) , como el siguiente:
getAllForRoommates(flatId: string, userId: string) { return this.firestore .collection('flats') .doc(flatId) .collection('cleaningDuties') .valueChanges() .pipe( mergeMap((duties) => // The `forkJoin` will emit its value only after all inner observables have been completed. forkJoin( duties.map((duty) => // For each duty, fetch the cleanerData, and append the result to the duty itself: this.userService.getPublicUserById(duty.currentCleaner.id).pipe( take(1), // to complete the sub observable after getting the value from it. map((cleanerData) => ({ ...duty, cleanerData })) ) ) ) ) ); }En primer lugar, le sugiero que mejore la escritura en su aplicación, eso lo ayudará a ver el problema con más claridad. Para mi respuesta, supondré que tiene 3 interfaces definidas CurrentCleaner, CleanerData y RoomData, que son solo:
interface RoomData { currentCleaner: CurrentCleaner cleanerData: CleanerData }Luego, puede escribir una función de ayuda para recuperar los datos de la habitación de un limpiador actual:
getRoomData$(currentCleaner: CurrentCleaner): Observable<RoomData> { return this.userService.getPublicUserById(currentCleaner.id).pipe( map(cleanerData => ({ currentCleaner, cleanerData })) ) }Como puede ver, estas funciones toman un limpiador actual, obtienen los datos del limpiador y los mapean en un objeto RoomData.
Tener este próximo paso es definir su función principal
getAllForRoommates(flatId: string, userId: string): Observable<RoomData[]> { return this.firestore .collection('flats') .doc(flatId) .collection('cleaningDuties') .valueChanges() .pipe( mergeMap((duties) => { // here we map all duties to observables of roomData let roomsData: Observable<RoomData>[] = duties.map(duty => this.getRoomData$(duty.currentCleaner)); // and use the forkJoin to convert Observable<RoomData>[] to Observable<RoomData[]> return forkJoin(roomsData) }), ); }Creo que de esta manera es fácil entender lo que está haciendo el código.
switchMap o mergeMap . En mi opinión, cancelaría el observable existente cuando valueChanges() . Vea aquí la diferencia entre ellos.Array#map con la función forkJoin de forkJoin para activar las solicitudes en paralelo.map RxJS con sintaxis de desestructuración para agregar propiedades adicionales a los objetos existentes.any tipo.Prueba lo siguiente
getAllForRoommates(flatId: string, userId: string) { return this.firestore .collection('flats') .doc(flatId) .collection('cleaningDuties') .valueChanges() .pipe( switchMap((duties: any) => forkJoin( duties.map((duty: any) => // <-- `Array#map` method this.userService.getPublicUserById(duty.currentCleaner.id).pipe( map((data: any) => ({ // <-- RxJS `map` operator ...duty, // <-- the `currentCleaner` property cleanerData: data // <-- new `cleanerData` property })) ) ) ) ) ); }