在某些数据的映射中获取异步资源的正确方法是什么?
What is the correct way to obtain an asynchronous resource in the mapping of some data?
根据以下数据集,我尝试用我可以在模板中使用的值替换 logoUrl 属性 的值,这是 S3 中受保护的 blob 资源
[
{
id: 2,
name: "ABC",
logoUrl: null,
},
{
id: 1,
name: "DEF",
logoUrl:
"https://assets.demo.app.net/bucket/154F460F8FAE344F13C29962931CBD3C1F1384F7A9DF9F548D23CF4AFF5E6811-some-name.jpeg",
},
{
id: 3,
name: "GHI",
logoUrl:
"https://assets.demo.app.net/bucket/4CA2F69627B51F7F07A39642DB3538A3D55564891F456528067B47DCEED61B4F-some-name.png",
},
{
id: 6,
name: "JKL",
logoUrl: null,
},
];
为此我尝试实现以下解决
@Injectable({
providedIn: 'root',
})
export class FetchDataResolver implements Resolve<Model[]> {
constructor(
private dataService: DataService,
private s3Service: S3Service
) {}
resolve(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot
): Observable<Model[]> {
return this.dataService
.getAll()
.pipe(
map((data) => data.sort((a, b) => a.name.localeCompare(b.name)))
);
}
}
我要解决的问题是.pipe()
里面如何映射从S3
获取资源的响应,然后替换记录。因为第一个动作是异步操作。
S3 服务有两个方法,一个是负责获取资源是一个blob,另一个是负责获取一个url,我可以在模板中使用它来显示图像。服务如下
@Injectable({
providedIn: 'root',
})
export class S3Service {
constructor(
private domSanitizer: DomSanitizer,
private httpClient: HttpClient
) {}
getResource(resource: string): Observable<Blob> {
return this.httpClient.get(resource, { responseType: 'blob' });
}
getSourceFromBlob(blob: Blob): SafeUrl {
return this.domSanitizer.bypassSecurityTrustUrl(URL.createObjectURL(blob));
}
}
预期结果类似如下
[
{
id: 2,
name: "ABC",
logoUrl: null,
},
{
id: 1,
name: "DEF",
logoUrl:
"blob:http://localhost:4200/e9113189-d08e-485c-b61a-5a8f5f4a0fdd",
},
{
id: 3,
name: "GHI",
logoUrl:
"blob:http://localhost:4200/e9113189-d08e-485c-b61a-5a8f5f4a0fdd",
},
{
id: 6,
name: "JKL",
logoUrl: null,
},
];
让我们尝试对情况有一个更高层次的了解。
你有一个流,它给你 Observable<Model[]>
。
从该流中,您想获得一个具有完全相同形状但已更新 1 属性 (logoUrl
) 的新流。
为此,您需要为数组的每个项目执行异步操作。
在这种情况下,几乎是第一个考虑它的运营商 forkJoin
。该运算符让您传递一组可观察对象,并将返回一个包含所有数组的流。所以换句话说:传递一个 Array<Observable<T>>
你会得到 Observable<Array<T>>
.
如果您很难想到那个,而您又熟悉 Promise.all
,那么它非常相似。
return this.dataService.getAll().pipe(
map((data) => data.sort((a, b) => a.name.localeCompare(b.name))),
switchMap((data) => {
// for each entry...
const dataWithSourceLogoUrl$ = data.map((x) => {
this.s3Service
// let's fetch the source logo
.getSourceFromBlob(x.logoUrl)
// and merge it back to the original object
.pipe(pipe(map((sourceLogoUrl) => ({ ...x, logoUrl: sourceLogoUrl }))));
});
// finally, use forkJoin to start all the streams we prepared above
return forkJoin(dataWithSourceLogoUrl$);
})
);
希望代码中的注释足够了,但如果不够请告诉我,我会编辑以解释更多。
根据以下数据集,我尝试用我可以在模板中使用的值替换 logoUrl 属性 的值,这是 S3 中受保护的 blob 资源
[
{
id: 2,
name: "ABC",
logoUrl: null,
},
{
id: 1,
name: "DEF",
logoUrl:
"https://assets.demo.app.net/bucket/154F460F8FAE344F13C29962931CBD3C1F1384F7A9DF9F548D23CF4AFF5E6811-some-name.jpeg",
},
{
id: 3,
name: "GHI",
logoUrl:
"https://assets.demo.app.net/bucket/4CA2F69627B51F7F07A39642DB3538A3D55564891F456528067B47DCEED61B4F-some-name.png",
},
{
id: 6,
name: "JKL",
logoUrl: null,
},
];
为此我尝试实现以下解决
@Injectable({
providedIn: 'root',
})
export class FetchDataResolver implements Resolve<Model[]> {
constructor(
private dataService: DataService,
private s3Service: S3Service
) {}
resolve(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot
): Observable<Model[]> {
return this.dataService
.getAll()
.pipe(
map((data) => data.sort((a, b) => a.name.localeCompare(b.name)))
);
}
}
我要解决的问题是.pipe()
里面如何映射从S3
获取资源的响应,然后替换记录。因为第一个动作是异步操作。
S3 服务有两个方法,一个是负责获取资源是一个blob,另一个是负责获取一个url,我可以在模板中使用它来显示图像。服务如下
@Injectable({
providedIn: 'root',
})
export class S3Service {
constructor(
private domSanitizer: DomSanitizer,
private httpClient: HttpClient
) {}
getResource(resource: string): Observable<Blob> {
return this.httpClient.get(resource, { responseType: 'blob' });
}
getSourceFromBlob(blob: Blob): SafeUrl {
return this.domSanitizer.bypassSecurityTrustUrl(URL.createObjectURL(blob));
}
}
预期结果类似如下
[
{
id: 2,
name: "ABC",
logoUrl: null,
},
{
id: 1,
name: "DEF",
logoUrl:
"blob:http://localhost:4200/e9113189-d08e-485c-b61a-5a8f5f4a0fdd",
},
{
id: 3,
name: "GHI",
logoUrl:
"blob:http://localhost:4200/e9113189-d08e-485c-b61a-5a8f5f4a0fdd",
},
{
id: 6,
name: "JKL",
logoUrl: null,
},
];
让我们尝试对情况有一个更高层次的了解。
你有一个流,它给你 Observable<Model[]>
。
从该流中,您想获得一个具有完全相同形状但已更新 1 属性 (logoUrl
) 的新流。
为此,您需要为数组的每个项目执行异步操作。
在这种情况下,几乎是第一个考虑它的运营商 forkJoin
。该运算符让您传递一组可观察对象,并将返回一个包含所有数组的流。所以换句话说:传递一个 Array<Observable<T>>
你会得到 Observable<Array<T>>
.
如果您很难想到那个,而您又熟悉 Promise.all
,那么它非常相似。
return this.dataService.getAll().pipe(
map((data) => data.sort((a, b) => a.name.localeCompare(b.name))),
switchMap((data) => {
// for each entry...
const dataWithSourceLogoUrl$ = data.map((x) => {
this.s3Service
// let's fetch the source logo
.getSourceFromBlob(x.logoUrl)
// and merge it back to the original object
.pipe(pipe(map((sourceLogoUrl) => ({ ...x, logoUrl: sourceLogoUrl }))));
});
// finally, use forkJoin to start all the streams we prepared above
return forkJoin(dataWithSourceLogoUrl$);
})
);
希望代码中的注释足够了,但如果不够请告诉我,我会编辑以解释更多。