【问题标题】:Populate data in an Observable based on data in the Observable itself根据 Observable 本身的数据填充 Observable 中的数据
【发布时间】:2020-11-28 00:18:23
【问题描述】:

首先:我是 Angular 的新手,所以这里的术语可能是错误的。我希望我能得到理解。

我正在使用 API。端点 A 暴露 Tickets;这些Tickets 包含Attachments 的数组(0-*)。那些Attachments 不完整;在数据模型中,它们包含基础文件内容的 base64 字符串,但为了减少一般负载,Attachments 仅在从端点 B 获取时才完成。但是为了从端点 B 获取 Attachment,我需要 @ 987654330@'s id,我只能从端点 A 获得。

我需要做的是我从端点 A 获取 attachment.id,使用该 id 从端点 B 获取适当的 Attachment,并将端点 A 的结果中的 Attachments 替换为端点的结果B. 但是,由于Attachments 被检索为Observable,因此我不能简单地分配值。

以下 sn-p 是解决此问题的几种尝试之一,并且能够从 API 获取 Attachments,但它们没有按照我想要的方式分配。

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值被写入控制台,因此至少部分代码可以按我的意愿工作。例如,对于文本/纯文本类型文件,我可以将值“SGV5IHRoZXJlLCByYW5kb20gcGVyc29uIGZyb20gU3RhY2tPdmVyZmxvdw”写入控制台,该值与我通过 Postman 手动执行查询时找到的值匹配。

我怀疑我正在尝试将 Observable (attachmentFilled) 分配给 Attachment[],但 TS 并未警告非法类型转换。查看 Chrome DevTools 中的网络选项卡,我看不到对getAttachments 的调用,只有对getTickets 的调用。 TicketAttachment 的数据模型是:

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
}

我正在使用以下模板来创建下载文件链接(受到angular-file-saver download base64 file using 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>

生成以下 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>

(受Angular2 Base64 sanitizing unsafe URL value 启发的网址清理似乎将空base64content 强制为undefined。在清理之前,现在有undefined 的地方将一无所有。

getAttachment 端点只接受attachment.id 作为输入。

我曾尝试以这种方式在Transform data in an 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' 不可分配给类型 'Observable'。类型 'Attachment[]' 与类型 'Ticket' 没有共同的属性”

【问题讨论】:

    标签: angular typescript


    【解决方案1】:

    我认为这是一个在这里经常被问到的经典问题。

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

    【讨论】:

    • 如果经常被问到,我一定是在我的命名法中太离谱了,以至于我找不到它。感谢您再次回答 - 它第一次工作没有任何问题。那里有一些有趣的语法,“{ ...”有什么作用? (文档链接就足够了)。
    • 没有通用词可以找到我的意思的其他问题。这只是我的经验,许多问题都归结为这个解决方案。 ... 是扩展运算符。它包括展开对象的所有属性。进入以 { 开头的新对象
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-12-29
    • 1970-01-01
    • 1970-01-01
    • 2020-05-22
    • 2023-02-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多