Wypełnij dane w Observable na podstawie danych w samym Observable
Po pierwsze: jestem nowy w Angular, więc podane tutaj terminy mogą być nieprawidłowe. Mam nadzieję, że uda mi się zrozumieć.
Używam API. Punkt końcowy A ujawnia Ticket
s; te Ticket
s przechowują tablicę (0- *) Attachment
s. Te Attachment
nie są kompletne; w modelu danych zawierają ciąg base64 zawartości pliku bazowego, ale aby zmniejszyć ogólne obciążenie, Attachment
s są kompletne tylko po pobraniu Attachment
z punktu końcowego B.Ale aby pobrać z punktu końcowego BI potrzebuję tych Attachment
, id
które mogę uzyskać tylko z punktu końcowego A.
Muszę się zdarzyć, że pobieram attachment.id
z punktu końcowego A, używam tego identyfikatora, aby pobrać odpowiednią wartość Attachment
z punktu końcowego B i zastępuję Attachment
s w wyniku z punktu końcowego A wynikiem z punktu końcowego Attachment
B.Jednak ponieważ są one pobierane tak Observable
, jak mogę ' Po prostu przypisz wartość.
Poniższy fragment kodu jest jedną z kilku prób rozwiązania tego problemu i jest w stanie pobrać je Attachment
z interfejsu API, ale nie są one przypisane tak, jak chciałem.
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
zwraca plik Observable<Ticket>
. getAttachment
zwraca plik Observable<Attachment>
.
Jak widać, zacząłem debugować przez logowanie (ponieważ aplikacja kątowa jest hostowana w większym systemie, który zapewnia również autoryzację na punktach końcowych, nie mogę tak naprawdę debugować we właściwy sposób ...) i zapisuje się wartość base64 do konsoli, więc przynajmniej niektóre części kodu działają tak, jak chcę. Na przykład dla pliku tekstowego / zwykłego mogę uzyskać wartość „SGV5IHRoZXJlLCByYW5kb20gcGVyc29uIGZyb20gU3RhY2tPdmVyZmxvdw” zapisaną na konsoli, która odpowiada wartości znalezionej podczas wykonywania zapytania ręcznie za pośrednictwem programu Postman.
Podejrzewam, że próbuję przypisać Observable
( attachmentFilled
) do, Attachment[]
ale TS nie ostrzega o nielegalnych konwersjach typów. Patrząc na kartę Sieć w Chrome DevTools, nie widzę wywołania getAttachments
tylko do wywołań getTickets
. Modele danych dla Ticket
i Attachment
to:
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
}
Używam następującego szablonu do tworzenia linków do plików do pobrania (ogromnie zainspirowany plikiem base64 do pobierania plików angular-file-saver przy użyciu 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>
co skutkuje następującym kodem HTML (zwróć uwagę, że wypełniane są tylko dane z punktu końcowego 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>
( Oczyszczanie adresu URL , zainspirowane czyszczeniem niebezpiecznego adresu URL przez Angular2 Base64 , wydaje się wymuszać na zerowej zawartości base64 undefined
. Przed santytyzacją nie byłoby niczego tam, gdzie jest teraz undefined
).
Punkt getAttachment
końcowy akceptuje tylko attachment.id
jako dane wejściowe.
Próbowałem zaimplementować rozwiązanie w Transform data in an Observable w następujący sposób:
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))])));
}
Ale bez awail: this.ticket$
zgłasza błąd „Typu„ Observable <Attachment []> ”nie można przypisać do typu„ Observable ”. Typ„ Attachment [] ”nie ma wspólnych właściwości z typem„ Ticket ””
Odpowiedzi
Myślę, że jest to klasyczny problem, o który tutaj zadaje się bardzo często.
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})),
);
})
);
}