Remplir les données dans un observable en fonction des données de l'observable lui-même
Tout d'abord: je suis nouveau sur Angular, donc les termes ici peuvent être faux. J'espère pouvoir faire passer la compréhension.
Je consomme une API. Le point final A expose Ticket
s; ces Ticket
s contiennent un tableau (0- *) de Attachment
s. Ces questions Attachment
ne sont pas complètes; dans le modèle de données, ils contiennent la chaîne base64 du contenu du fichier sous-jacent, mais pour réduire la charge générale, les Attachment
s ne sont complets que lorsqu'ils sont extraits d'Endpoint B.Mais pour récupérer un Attachment
depuis Endpoint BI, j'ai besoin Attachment
de ceux id
que je ne peux obtenir à partir du point final A.
Ce que je dois arriver, c'est que je récupère à attachment.id
partir du point final A, que j'utilise cet identifiant pour récupérer l'approprié Attachment
du point final B et que je remplace Attachment
s dans le résultat du point final A par le résultat du point final B. Cependant, puisque les Attachment
s sont récupérés comme Observable
je peux ' t attribuez simplement la valeur.
L'extrait de code suivant est l'une des nombreuses tentatives pour résoudre ce problème et est capable d'obtenir les Attachment
s de l'API mais ils ne sont pas attribués comme je le souhaitais.
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
renvoie un Observable<Ticket>
. getAttachment
renvoie un Observable<Attachment>
.
Comme on peut le voir, j'ai commencé à déboguer en journalisant (puisque l'application angulaire est hébergée dans un système plus grand qui fournit également une autorisation contre les points de terminaison, je ne peux pas vraiment la déboguer de la bonne manière ...) et la valeur base64 est écrite à la console, donc au moins certaines parties du code fonctionnent comme je le souhaite. Par exemple, pour un fichier de type texte / brut, je peux obtenir la valeur "SGV5IHRoZXJlLCByYW5kb20gcGVyc29uIGZyb20gU3RhY2tPdmVyZmxvdw" écrite sur la console qui correspond à la valeur trouvée lorsque j'exécute la requête manuellement via Postman.
Je soupçonne que j'essaie d'attribuer un Observable
( attachmentFilled
) au Attachment[]
mais TS ne met pas en garde contre les conversions de type illégales. En regardant l'onglet Réseau dans Chrome DevTools, je ne vois pas l'appel getAttachments
uniquement pour les appels à getTickets
. Les modèles de données pour Ticket
et Attachment
sont:
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
}
J'utilise le modèle suivant pour créer les liens de fichier de téléchargement (largement inspiré du fichier base64 de téléchargement angular-file-saver à l'aide de 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>
ce qui donne le code HTML suivant (notez comment seules les données du point de terminaison A sont remplies):
<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 désinfection d'URL, inspirée de la valeur d'URL non sécurisée de nettoyage Angular2 Base64 , semble forcer le contenu base64 nul à undefined
. Avant la santitisation, il n'y aurait rien là où il y en a maintenant undefined
).
Le getAttachment
point de terminaison accepte uniquement le attachment.id
comme entrée.
J'ai essayé d'implémenter la solution dans Transformer les données de cette manière:
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))])));
}
Mais pas de problème: this.ticket$
renvoie une erreur "Le type 'Observable <Attachment []>' n'est pas assignable au type 'Observable'. Le type 'Attachment []' n'a pas de propriétés en commun avec le type 'Ticket'"
Réponses
Je pense que c'est un problème classique posé très souvent ici.
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})),
);
})
);
}