根据可观察量本身中的数据填充可观察量中的数据



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

我正在使用一个 API。终结点 A 公开Tickets;这些Ticket保存一个数组 (0-*) 的Attachments。这些Attachment是不完整的;在数据模型中,它们包含基础文件内容的 base64 字符串,但为了减少一般负载,Attachment只有在从端点 B 获取时才是完整的。但是为了从端点 B 获取Attachment,我需要Attachmentid我只能从端点 A 获取。

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

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

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>

可以看出,我已经开始通过日志记录进行调试(因为角度应用程序托管在一个更大的系统中,该系统还提供针对端点的授权,我无法真正以正确的方式调试它......)并且 base64 值被写入控制台,所以至少代码的某些部分可以按照我想要的方式工作。例如,对于文本/纯文本类型文件,我可以将值"SGV5IHRoZXJlLCByYW5kb20gcGVyc29uIGZyb20gU3RhY2tPdmVyZmxvdw"写入控制台,这与我通过Postman手动执行查询时找到的值相匹配。

我怀疑我正在尝试为Attachment[]分配Observable(attachmentFilled),但 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
}

我使用以下模板来创建下载文件链接(受到使用 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 值,似乎强制空 base64contentundefined。在santiization之前,现在有undefined的地方什么都没有)。

getAttachment终结点仅接受attachment.id作为输入。

我尝试以这种方式在"以可观察对象转换数据"中实现解决方案:

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$抛出错误"类型'可观察<附件[]>'不能分配给类型'可观察'。类型'附件[]'与类型'票证'没有共同的属性">

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

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

最新更新