Wypełnij dane w Observable na podstawie danych w samym Observable

Nov 28 2020

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 Tickets; te Tickets przechowują tablicę (0- *) Attachments. Te Attachmentnie są kompletne; w modelu danych zawierają ciąg base64 zawartości pliku bazowego, ale aby zmniejszyć ogólne obciążenie, Attachments są kompletne tylko po pobraniu Attachmentz punktu końcowego B.Ale aby pobrać z punktu końcowego BI potrzebuję tych Attachment, idktóre mogę uzyskać tylko z punktu końcowego A.

Muszę się zdarzyć, że pobieram attachment.idz punktu końcowego A, używam tego identyfikatora, aby pobrać odpowiednią wartość Attachmentz punktu końcowego B i zastępuję Attachments w wyniku z punktu końcowego A wynikiem z punktu końcowego AttachmentB.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 Attachmentz 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;
    })
  );
}

getTicketzwraca plik Observable<Ticket>. getAttachmentzwraca 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 getAttachmentstylko do wywołań getTickets. Modele danych dla Ticketi Attachmentto:

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 getAttachmentkońcowy akceptuje tylko attachment.idjako 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

1 MoxxiManagarm Nov 28 2020 at 12:25

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})),
      );
    })
  );
}