【发布时间】:2019-12-25 20:28:36
【问题描述】:
我正在用 Angular 5 重写一个从服务器获取数据并下载一些 pdf 文件的应用程序。服务端是一个soap接口,有3个api:
1 - 获取广告系列列表
2 - 按活动 ID 获取文件数组
3 - 通过文件 id 获取文件
我已经用纯 XMLHTTPRequest 和一些递归函数完成了这项工作,这些函数填充了“campaigns”对象、提取封面并一个接一个地保存所有文件。
我现在想要的是避免回调地狱并使用httpclient和angular中可用的observables来获得相同的结果,但是经过两天对rxjs库的研究,我仍然不明白如何去做.
我已经使用 httpclient 成功获取了数据,并且我已经对 observable opertors 做了很多尝试。 事件流程示意图为:
获取广告系列 xml =>
将 xml 数据转换为一系列活动(使用地图运算符完成)=>
为每个活动获取 filesInfo =>
将 xml 数据转换为 filesInfo 数组 =>
为每个活动的每个文件获取该文件 =>
提取文件封面并保存文件。
我被困在这里:
getCampaigns():Observable<Campaign[]>{
return this.getRawDataProvider.getRawData('campaignsList').pipe(
map(rawCampaigns => {return this.createCampaignArray(rawCampaigns)})
)
}
this.getCampaigns().subscribe(result => {
console.log(result) //correctly shows an array of campaigns (without the files of course)
})
现在呢?我应该使用“from”运算符从数组中创建新的 observable 吗?还是在阵列上进行一个 foreach 并为每个活动启动一个新的 http 客户端请求?最后,我需要一系列这样的活动:
[
{
name: 'campaign name',
id: 1234,
files:[
{
name:'file name',
id: 4321,
cover: 'base64string'
}
]
}
]
我需要文件一个接一个地下载,而不是并行下载
编辑: 还要感谢 dmcgrandle 的回答,我弄清楚了一些事情,经过几次尝试,我得出了这个结论:
updateCampaigns():Observable<Campaign[]>{
let updatedCampaigns:Campaign[];
return this.getRawDataProvider.getRawData( 'campaignList' ).pipe(
map( rawCampaigns => {
updatedCampaigns = this.createCampaignsArray( rawCampaigns );
return updatedCampaigns;
} ),
switchMap( _campaigns => from( _campaigns ).pipe(
concatMap( _campaign => this.getRawDataProvider.getRawData( 'campaignDocuments', _campaign.id ) )
) ),
map((rawDocuments,i)=>{
updatedCampaigns[ i ].documents = this.createDocumentsArray( rawDocuments );
return;
} ),
reduce( () => {
let { campaigns, documentsToDowloadIdList } = this.mergeWithStoredData( updatedCampaigns );
updatedCampaigns = campaigns;
return documentsToDowloadIdList;
} ),
switchMap( list => from( list ).pipe(
concatMap( documentId => this.getRawDataProvider.getRawData( 'document', documentId ).pipe(
map( b64file => { return { documentId:documentId, b64file:b64file } } )
) )
) ),
concatMap( file => this.saveFile( file.b64file, file.fileId ) ),
tap( fileInfo => {
updatedCampaigns.forEach( campaign => { campaign.documents.forEach( document => {
if( fileInfo.fileId == document.id ) {
document.cover = fileInfo.b64cover;
document.numPages = fileInfo.numPages;
}
} ) } )
} ),
reduce( () => {
this.storageservice.set( 'campaigns' , updatedCampaigns );
return updatedCampaigns;
} )
)
}
请注意,我已经修改了 saveFile 函数,现在它返回一个 observable。 现在,这有效......我想知道是否有更简单的方法以及我是否遗漏了什么。 我也在尝试使用 catchError 运算符处理错误,但是如果我使用 throw,则 observable 的类型将变为 void,我必须将类型“any”而不是“Campaign []”以使其工作。 最后一件事,我希望这个可观察对象在每次使用新文档更新时返回活动对象,以便在下载文件时显示封面,而不是最后一起显示......可以这样做吗?
【问题讨论】:
-
如果以下答案对您有所帮助,请考虑将其标记为正确和/或投票。谢谢。 :)
标签: nested observable angular-httpclient