Füllen Sie Daten in einem Observable basierend auf Daten im Observable selbst
Zunächst einmal: Ich bin neu bei Angular, daher könnten die Begriffe hier falsch sein. Ich hoffe, ich kann das Verständnis durchbringen.
Ich konsumiere eine API. Endpunkt A macht Ticket
s sichtbar ; Diese Ticket
s enthalten ein Array (0- *) von Attachment
s. Diese Attachment
s sind nicht vollständig; Im Datenmodell enthalten sie die base64-Zeichenfolge des Inhalts der zugrunde liegenden Datei. Um die allgemeine Auslastung zu verringern, sind die Attachment
s jedoch nur dann vollständig, wenn sie von Endpoint B abgerufen werden. Um jedoch eine Attachment
von Endpoint BI abzurufen, benötigen Sie die Attachment
s, id
die ich nur erhalten kann vom Endpunkt A.
Was ich attachment.id
tun muss, ist, dass ich von Endpunkt A abrufe, diese ID verwende, um das entsprechende Ergebnis Attachment
von Endpunkt B abzurufen , und Attachment
s im Ergebnis von Endpunkt A durch das Ergebnis von Endpunkt B Attachment
ersetze . Da die s jedoch so weit wie möglich abgerufen werden. Observable
' t einfach nur den Wert zuweisen.
Das folgende Snippet ist einer von mehreren Versuchen, dies zu lösen, und kann die Attachment
s von der API abrufen, aber sie sind nicht so zugewiesen, wie ich es wollte.
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
gibt ein zurück Observable<Ticket>
. getAttachment
gibt ein zurück Observable<Attachment>
.
Wie zu sehen ist, habe ich mit dem Debuggen durch Protokollierung begonnen (da die eckige App in einem größeren System gehostet wird, das auch die Autorisierung für die Endpunkte bietet, kann ich sie nicht richtig debuggen ...) und der base64-Wert wird geschrieben an die Konsole, so dass zumindest einige Teile des Codes so funktionieren, wie ich es möchte. Zum Beispiel kann ich für eine Text- / Nur-Typ-Datei den Wert "SGV5IHRoZXJlLCByYW5kb20gcGVyc29uIGZyb20gU3RhY2tPdmVyZmxvdw" erhalten, der in die Konsole geschrieben wurde und dem Wert entspricht, der gefunden wurde, wenn ich die Abfrage manuell über Postman ausführe.
Ich vermute, dass ich versuche, dem ein Observable
( attachmentFilled
) zuzuweisen , Attachment[]
aber TS warnt nicht vor illegalen Typkonvertierungen. Wenn ich in Chrome DevTools auf die Registerkarte "Netzwerk" schaue, kann ich den Anruf getAttachments
nur für die Anrufe an nicht sehen getTickets
. Die Datenmodelle für Ticket
und Attachment
sind:
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
}
Ich verwende die folgende Vorlage, um die Download- Dateilinks zu erstellen (stark inspiriert von der Angle-File-Saver-Download-Base64-Datei mit 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>
Dies führt zu folgendem HTML-Code (beachten Sie, dass nur die Daten von Endpunkt A ausgefüllt werden):
<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>
(Die URL- Bereinigung, inspiriert von der Angular2 Base64-Bereinigung eines unsicheren URL-Werts , scheint den Null-Base64-Inhalt zu erzwingen undefined
. Vor der Santitisierung würde es nichts geben, wo es jetzt ist. undefined
)
Der getAttachment
Endpunkt akzeptiert nur die attachment.id
als Eingabe.
Ich habe versucht, die Lösung in Transform data in einem Observable folgendermaßen zu implementieren :
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))])));
}
Aber ohne zu beklagen: löst this.ticket$
einen Fehler aus "Der Typ 'Observable <Attachment []>' kann nicht dem Typ 'Observable' zugewiesen werden. Der Typ 'Attachment []' hat keine gemeinsamen Eigenschaften mit dem Typ 'Ticket'."
Antworten
Ich denke, das ist ein klassisches Problem, das hier sehr oft gestellt wird.
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})),
);
})
);
}