Angular 2 递归 http.gets 未知深度
Angular 2 recursive http.gets of unknown depth
我一直在使用类似下面的模式在 Angular2 中将 http.gets 链接在一起,以从两层深的文件夹(所有伪类型脚本)的层次结构中检索信息:
myObservable = this.myService.getSubFolders(topFolderUrl)
.switchMap(subFolders=> this.myService.getInfoFromSubFolders(subFolders))
.map(subFolders=> => {
...do stuff with subFolders...
return subFolders;
}
);
myService 看起来像这样:
getSubFolders(topFolderUrl): Observable<Folder[]> {
return this.http.get(topFolderUrl)
.map(res => {
let body = res.json();
let foldersToReturn: Folder[] = [];
for (let subfolder of body.subFolders) {
let tempFolder = new Folder;
tempFolder.url = subfolder.url;
tempFolder.otherProps = subfolder.otherPropValue;
}
return foldersToReturn;
}
.catch(this.handleError);
}
getInfoFromSubFolders(subFolders:Folder[]): Observable<Folder[]> {
let calls: any[] = [];
for (let folder of subFolders:Folder){
calls.push(
this.http.get(folder.url)
);
var subject = new Subject<Folder[]>(); //see: for why Subject
Observable.forkJoin(calls).subscribe((res: any) => {
let foundFolder = subFolders.find(folder=> {
return response.url.indexOf(folder.url)!==-1;
});
for (let response of res){
let bodyAsJson = JSON.parse(response._body);
foundFolder.otherProps = bodyAsJson.otherPropValue;
}
subject.next(subFolders);
});
return subject;
}
然后我使用 | 订阅 myObservable我的模板中的异步管道。 myObservable 中的对象最终是这样的:
{
"url": "topFolderUrl",
"otherProps": "otherPropsValue",
"subFolders": [
{
"url": "subFolder1Url",
"otherProps": "otherPropsValue"
},
{
"url": "subFolder2Url",
"otherProps": "otherPropsValue",
}
]
}
但是,这依赖于这种文件夹结构正好是两层深 - 不多也不少我有两个相关问题:
- 我将如何重构它以允许我递归地沿着一系列文件夹 n 层深入 - 我一次只能请求一层 - 即每个子文件夹有"subFolders":[]等
- 如果没有子文件夹,我将如何让系统处理?即不在 .switchMap
中调用 getInfoFromSubFolders
我感觉这是一个非常常见的场景,因此它可能对很多人都有用。
如有指点,不胜感激
我认为 expand
运算符可以在这方面为您提供帮助,因为它解决了此类用例中的递归问题。
有关详细信息,请参阅此问题:
我在头疼实现时遇到了这个post
an other problem to do with recursivity and Observables。幸运的是,我现在可以回答你的问题了。
问题要解决
从作为参数提供的文件夹开始,您想访问所有 sub-folders。对于每个 sub-folder,访问所有 sub-folders 并无限重复此过程。
为了回答你关于重构和模型的问题,这个问题是一个典型的tree traversal,它的选项可用于数据结构。
您没有提供有关您的文件夹的详细信息,但让我们做一些假设。您的 class 具有以下属性:
- parent: 文件夹
- children: 文件夹[]
您目前有一项针对给定文件夹的 returns children 服务。假设方法签名是:
myObservable: Observable<Folder[]> = this.myService.getSubFolders(aFolder);
一种使用递归的方法
为了实现递归方法,它有助于创建一个上下文,该上下文将包含(正在构建的)结果和递归状态所需的任何其他属性:FolderTraversalContext。
processSubFolders(ctx: FolderTraversalContext): Observable<FolderTraversalContext> {
return this.getSubFolders(ctx.folder)
.map( folders: Folder[] => {
folders.forEach( f => {
f.parent = folder;
ctx.toVisit.push(f);
});
folder.children = folders;
//Prepare for next cycle
ctx.complete = ctx.toVisit.length() === 0;
if (! ctx.complete) {
ctx.folder = ctx.toVisit[0];
ctx.toVisit.splice(0,1);
}
return ctx;
}
}
扩展运算符的使用
以反应方式实现递归的方法如下所示:
readAllSubfolders(topFolder: Folder): Observable<Folder> {
const ctx = new FolderTraversalContext();
ctx.topFolder = topFolder;
ctx.complete = false;
ctx.toVisit = [];
ctx.folder = topFolder;
return Observable.of(ctx)
.expand( ctx => {
return (ctx.completed) ? Observable.empty() : this.processSubFolders(ctx);
})
.map(ctx => ctx.topFolder);
}
快点!
我一直在使用类似下面的模式在 Angular2 中将 http.gets 链接在一起,以从两层深的文件夹(所有伪类型脚本)的层次结构中检索信息:
myObservable = this.myService.getSubFolders(topFolderUrl)
.switchMap(subFolders=> this.myService.getInfoFromSubFolders(subFolders))
.map(subFolders=> => {
...do stuff with subFolders...
return subFolders;
}
);
myService 看起来像这样:
getSubFolders(topFolderUrl): Observable<Folder[]> {
return this.http.get(topFolderUrl)
.map(res => {
let body = res.json();
let foldersToReturn: Folder[] = [];
for (let subfolder of body.subFolders) {
let tempFolder = new Folder;
tempFolder.url = subfolder.url;
tempFolder.otherProps = subfolder.otherPropValue;
}
return foldersToReturn;
}
.catch(this.handleError);
}
getInfoFromSubFolders(subFolders:Folder[]): Observable<Folder[]> {
let calls: any[] = [];
for (let folder of subFolders:Folder){
calls.push(
this.http.get(folder.url)
);
var subject = new Subject<Folder[]>(); //see: for why Subject
Observable.forkJoin(calls).subscribe((res: any) => {
let foundFolder = subFolders.find(folder=> {
return response.url.indexOf(folder.url)!==-1;
});
for (let response of res){
let bodyAsJson = JSON.parse(response._body);
foundFolder.otherProps = bodyAsJson.otherPropValue;
}
subject.next(subFolders);
});
return subject;
}
然后我使用 | 订阅 myObservable我的模板中的异步管道。 myObservable 中的对象最终是这样的:
{
"url": "topFolderUrl",
"otherProps": "otherPropsValue",
"subFolders": [
{
"url": "subFolder1Url",
"otherProps": "otherPropsValue"
},
{
"url": "subFolder2Url",
"otherProps": "otherPropsValue",
}
]
}
但是,这依赖于这种文件夹结构正好是两层深 - 不多也不少我有两个相关问题:
- 我将如何重构它以允许我递归地沿着一系列文件夹 n 层深入 - 我一次只能请求一层 - 即每个子文件夹有"subFolders":[]等
- 如果没有子文件夹,我将如何让系统处理?即不在 .switchMap 中调用 getInfoFromSubFolders
我感觉这是一个非常常见的场景,因此它可能对很多人都有用。
如有指点,不胜感激
我认为 expand
运算符可以在这方面为您提供帮助,因为它解决了此类用例中的递归问题。
有关详细信息,请参阅此问题:
我在头疼实现时遇到了这个post an other problem to do with recursivity and Observables。幸运的是,我现在可以回答你的问题了。
问题要解决 从作为参数提供的文件夹开始,您想访问所有 sub-folders。对于每个 sub-folder,访问所有 sub-folders 并无限重复此过程。
为了回答你关于重构和模型的问题,这个问题是一个典型的tree traversal,它的选项可用于数据结构。
您没有提供有关您的文件夹的详细信息,但让我们做一些假设。您的 class 具有以下属性:
- parent: 文件夹
- children: 文件夹[]
您目前有一项针对给定文件夹的 returns children 服务。假设方法签名是:
myObservable: Observable<Folder[]> = this.myService.getSubFolders(aFolder);
一种使用递归的方法
为了实现递归方法,它有助于创建一个上下文,该上下文将包含(正在构建的)结果和递归状态所需的任何其他属性:FolderTraversalContext。
processSubFolders(ctx: FolderTraversalContext): Observable<FolderTraversalContext> {
return this.getSubFolders(ctx.folder)
.map( folders: Folder[] => {
folders.forEach( f => {
f.parent = folder;
ctx.toVisit.push(f);
});
folder.children = folders;
//Prepare for next cycle
ctx.complete = ctx.toVisit.length() === 0;
if (! ctx.complete) {
ctx.folder = ctx.toVisit[0];
ctx.toVisit.splice(0,1);
}
return ctx;
}
}
扩展运算符的使用
以反应方式实现递归的方法如下所示:
readAllSubfolders(topFolder: Folder): Observable<Folder> {
const ctx = new FolderTraversalContext();
ctx.topFolder = topFolder;
ctx.complete = false;
ctx.toVisit = [];
ctx.folder = topFolder;
return Observable.of(ctx)
.expand( ctx => {
return (ctx.completed) ? Observable.empty() : this.processSubFolders(ctx);
})
.map(ctx => ctx.topFolder);
}
快点!