Complete los datos en un Observable basado en datos en el Observable mismo
En primer lugar: soy nuevo en Angular, por lo que los términos aquí pueden estar equivocados. Espero poder entenderlo.
Estoy consumiendo una API. El punto final A expone Ticket
s; estos Ticket
s contienen una matriz (0- *) de Attachment
s. Esos Attachment
s no están completos; en el modelo de datos que contienen la cadena de base 64 del contenido del archivo subyacente pero para reducir la carga general de los Attachment
s son sólo se completa cuando se tomaron de punto final B. Sin embargo, con el fin de ir a buscar una Attachment
de las de punto final de BI necesitan Attachment
's id
que sólo puedo conseguir desde el punto final A.
Lo que necesito que suceda es que obtengo attachment.id
del Endpoint A, uso esa identificación para buscar el approriate Attachment
del Endpoint B, y reemplace Attachment
s en el resultado del Endpoint A con el resultado del Endpoint B. Sin embargo, dado que los Attachment
s se recuperan como Observable
puedo ' Simplemente asigne el valor.
El siguiente fragmento es uno de los varios intentos de resolver esto y puede obtener los Attachment
mensajes de correo electrónico de la API, pero no están asignados de la manera que quería.
ngOnInit(): void {
console.log(`getting ticket for id ${this.id}`); this.ticket$ = this.ticketService.getTicket(this.id).pipe(
map((response:Ticket) => {
response.attachments.map((attachmentPartly:Attachment) => {
console.log(`getting attachment for id ${attachmentPartly.id}`); let attachmentFilled = this.ticketService.getAttachment(attachmentPartly.id); attachmentFilled.subscribe(att => console.log(`base64 content is: ${att.base64Content}`)); //for posterity: this line has been omitted in tests with no change in result
return attachmentFilled;
});
return response;
})
);
}
getTicket
devuelve un Observable<Ticket>
. getAttachment
devuelve un Observable<Attachment>
.
Como se puede ver, comencé a depurar registrando (dado que la aplicación angular está alojada dentro de un sistema más grande que también proporciona autorización contra los puntos finales, realmente no puedo depurarla de la manera correcta ...) y el valor base64 está escrito a la consola, por lo que al menos algunas partes del código funcionan como quiero. Por ejemplo, para un archivo de texto / sin formato, puedo obtener el valor "SGV5IHRoZXJlLCByYW5kb20gcGVyc29uIGZyb20gU3RhY2tPdmVyZmxvdw" escrito en la consola que coincide con el valor encontrado cuando ejecuto la consulta manualmente a través de Postman.
Sospecho que estoy tratando de asignar un Observable
( attachmentFilled
) al Attachment[]
pero TS no advierte sobre conversiones de tipos ilegales. Al mirar la pestaña Red en Chrome DevTools, no puedo ver la llamada getAttachments
solo a las llamadas a getTickets
. Los modelos de datos para Ticket
y Attachment
son:
export interface Ticket {
id?: string;
description?: string;
assignee?: string;
createdAt?: string;
resolvedAt?: string;
attachments?: Attachment[];
}
export interface Attachment {
name?: string; //contained in result from Endpoint A
id?: string; //contained in result from Endpoint A
relatesTo?: string;
type?: string; //contained in result from Endpoint A
sizeBytes?: string; //contained in result from Endpoint A
hash?: string;
url?: string;
base64Content?: string; //need to fetch this
}
Estoy usando la siguiente plantilla para crear los enlaces del archivo de descarga (muy inspirado por el archivo base64 de descarga de angular-file-saver usando FileSaver ):
<div *ngFor="let attachment of ticket.attachments" class="list-group-item">
<div class="d-flex w-100 justify-content-between">
<a [href]="'data:' + attachment.type + ';base64,' + attachment.base64Content | safeUrl" target="_blank" download="{{ attachment.name }}">{{ attachment.name }}</a>
<small>{{ attachment.sizeBytes | filesize }}</small>
</div>
<small class="mb-1">{{ attachment.type }}</small>
</div>
que da como resultado el siguiente html (observe cómo solo se completan los datos del punto final A):
<div _ngcontent-dmp-c28="" class="list-group-item">
<div _ngcontent-dmp-c28="" class="d-flex w-100 justify-content-between">
<a _ngcontent-dmp-c28="" target="_blank" download="test.txt" href="data:text/plain;base64,undefined">test.txt</a>
<small _ngcontent-dmp-c28="">43 B</small>
</div>
<small _ngcontent-dmp-c28="" class="mb-1">text/plain</small>
</div>
(La desinfección de URL, inspirada en el valor de URL inseguro de desinfección de Angular2 Base64 , parece forzar el contenido nulo de base64 a undefined
. Antes de la desinfección, no habría nada donde ahora hay undefined
).
El getAttachment
punto final solo acepta attachment.id
como entrada.
He intentado implementar la solución en Transform data de una manera observable de esta manera:
ngOnInit(): void {
console.log(`getting ticket for id ${this.id}`); this.ticket$ = this.ticketService.getTicket(this.id).pipe(
mergeMap((response: Ticket) => forkJoin([...response.attachments.map((attachment : Attachment) => this.ticketService.getAttachment(attachment.id))])));
}
Pero para no esperar: this.ticket$
arroja un error "El tipo 'Observable <Attachment []>' no se puede asignar al tipo 'Observable'. El tipo 'Attachment []' no tiene propiedades en común con el tipo 'Ticket'"
Respuestas
Creo que es un problema clásico que se plantea aquí muy a menudo.
ngOnInit(): void {
this.ticket$ = this.ticketService.getTicket(this.id).pipe( switchMap((response: Ticket) => { const filledAttechments$ = response.attachments.map(
(attachmentPartly: Attachment) => this.ticketService.getAttachment(attachmentPartly.id).pipe(
map(att => ({ ...attachmentPartly, base64Content: att.base64Content })),
),
);
return forkJoin(filledAttechments$).pipe(
map(filledAttachments => ({...response, attachments: filledAttachments})),
);
})
);
}