Mengisi data di Observable berdasarkan data di Observable itu sendiri

Nov 28 2020

Pertama: Saya baru mengenal Angular jadi istilah di sini mungkin salah. Saya harap saya bisa memahami sepenuhnya.

Saya menggunakan API. Endpoint A memperlihatkan Tickets; Tickets ini menampung sebuah array (0- *) dari Attachments. Itu Attachmentbelum lengkap; dalam model data mereka mengandung string base64 dari isi dari file yang mendasari tetapi untuk mengurangi beban umum Attachments hanya lengkap bila diambil dari Endpoint B. Tetapi untuk mengambil sebuah Attachmentdari Endpoint BI perlu Attachment's idyang saya hanya bisa mendapatkan dari Endpoint A.

Yang perlu saya lakukan adalah saya mengambil attachment.iddari Endpoint A, menggunakan id itu untuk mengambil approriate Attachmentdari Endpoint B, dan mengganti Attachments di hasil dari Endpoint A dengan hasil dari Endpoint B. Namun karena Attachments diambil sebisa Observablesaya ' t hanya cukup menetapkan nilainya.

Cuplikan berikut adalah salah satu dari beberapa upaya untuk menyelesaikan ini dan bisa mendapatkan Attachments dari API tetapi mereka tidak ditugaskan seperti yang saya inginkan.

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

getTicketmengembalikan sebuah Observable<Ticket>. getAttachmentmengembalikan sebuah Observable<Attachment>.

Seperti yang dapat dilihat, saya mulai men-debug dengan logging (karena aplikasi angular dihosting dalam sistem yang lebih besar yang juga memberikan otorisasi terhadap endpoint, saya tidak dapat benar-benar men-debug dengan cara yang benar ...) dan nilai base64 ditulis ke konsol, jadi setidaknya beberapa bagian kode berfungsi seperti yang saya inginkan. Misalnya untuk file tipe teks / biasa saya bisa mendapatkan nilai "SGV5IHRoZXJlLCByYW5kb20gcGVyc29uIGZyb20gU3RhY2tPdmVyZmxvdw" yang tertulis di konsol yang sesuai dengan nilai yang ditemukan saat saya menjalankan kueri secara manual melalui Postman.

Saya menduga bahwa saya mencoba untuk menetapkan Observable( attachmentFilled) ke Attachment[]tetapi TS tidak memperingatkan tentang konversi jenis ilegal. Melihat tab Jaringan di Chrome DevTools, saya tidak dapat melihat panggilan untuk getAttachmentshanya panggilan ke getTickets. Model data untuk Ticketdan Attachmentadalah:

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
}

Saya menggunakan template berikut untuk membuat tautan file unduhan (sangat terinspirasi oleh file base64 unduhan penghemat file sudut menggunakan 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>

yang menghasilkan html berikut (perhatikan bagaimana hanya data dari Endpoint A yang diisi):

<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>

( Pembersih url, terinspirasi oleh Angular2 Base64 membersihkan nilai URL yang tidak aman , tampaknya memaksa konten base64 nol ke undefined. Sebelum santitisasi tidak akan ada apa-apa di mana sekarang ada undefined).

Titik getAttachmentakhir hanya menerima attachment.idsebagai masukan.

Saya telah mencoba menerapkan solusi dalam Transformasi data dengan cara yang dapat diamati ini:

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

Tapi to no awail: this.ticket$throws an error "Type 'Observable <Attachment []>' is not assignable to type 'Observable'. Type 'Attachment []' tidak memiliki properti yang sama dengan jenis 'Tiket'"

Jawaban

1 MoxxiManagarm Nov 28 2020 at 12:25

Saya pikir itu adalah masalah klasik yang sering ditanyakan di sini.

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