Observable自体のデータに基づいてObservableにデータを入力します

Nov 28 2020

まず、Angularは初めてなので、ここでの用語は間違っている可能性があります。理解していただければ幸いです。

APIを使用しています。エンドポイントAはTicketsを公開します。これらTicketのsは、sの配列(0- *)を保持しAttachmentます。それらAttachmentは完全ではありません。データモデルでは、基になるファイルのコンテンツのbase64文字列が含まれていますが、一般的な負荷を軽減するためにAttachment、エンドポイントBからフェッチした場合にのみ完了します。ただし、AttachmentエンドポイントBIからフェッチするには、取得できるAttachmentidが必要です。エンドポイントAから。

私が行う必要があるのはattachment.id、エンドポイントAからフェッチし、そのIDを使用してAttachmentエンドポイントBから適切なものをフェッチし、AttachmentエンドポイントAからの結果のsをエンドポイントBからの結果に置き換えることです。ただし、AttachmentsはObservable可能な限り取得されるためです。t単に値を割り当てるだけです。

次のスニペットは、これを解決するためのいくつかの試みの1つでありAttachment、APIからを取得できますが、希望どおりに割り当てられていません。

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を返しますObservable<Ticket>getAttachmentを返しますObservable<Attachment>

ご覧のとおり、ロギングによってデバッグを開始し(Angularアプリはエンドポイントに対する承認も提供する大規模なシステム内でホストされているため、正しい方法でデバッグすることはできません...)、base64値が書き込まれますコンソールに接続するので、コードの少なくとも一部は希望どおりに機能します。たとえば、テキスト/プレーンタイプのファイルの場合、Postmanを介して手動でクエリを実行したときに見つかった値と一致する、コンソールに書き込まれた値「SGV5IHRoZXJlLCByYW5kb20gcGVyc29uIGZyb20gU3RhY2tPdmVyZmxvdw」を取得できます。

ObservableattachmentFilled)を割り当てようとしているのでAttachment[]はないかと思いますが、TSは不正な型変換について警告していません。Chrome DevToolsの[ネットワーク]タブを見ると、への呼び出しgetAttachmentsだけが表示されていませんgetTickets。以下のためのデータモデルTicketとはAttachment、次のとおりです。

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
}

次のテンプレートを使用して、ダウンロードファイルのリンクを作成しています(FileSaverを使用したangular-file-saverダウンロードbase64ファイルに大きく影響を受けています)。

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

これにより、次のhtmlが生成されます(エンドポイント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>

(安全でないURL値をサニタイズするAngular2 Base64に触発されたURLサニタイズは、null base64contentを強制するように見えます。サニタイズundefined前は、現在の場所には何もありませんundefined)。

getAttachmentエンドポイントは、唯一受け入れattachment.id、入力として。

私は次のようにObservableでデータを変換するソリューションを実装しようとしました:

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

しかし、何の問題もありません。this.ticket$「タイプ 'Observable <Attachment []>'はタイプ 'Observable'に割り当てることができません。タイプ 'Attachment []'にはタイプ 'Ticket'と共通のプロパティがありません」というエラーがスローされます。

回答

1 MoxxiManagarm Nov 28 2020 at 12:25

これは、ここでよく聞かれる古典的な問題だと思います。

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