Angular 2递归的未知深度的http.gets

时间:2016-08-07 10:01:17

标签: angular rxjs observable

我一直在使用类似下面的模式将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: http://stackoverflow.com/a/38668416/2235210 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",
    }
  ]
}

然而,这依赖于这个文件夹的结构正好两层深 - 不多也不少我有两个相关的问题:

  1. 我如何重构这个以允许我递归地沿着一系列文件夹 n 层深入工作 - 我一次只能请求一个层 - 即每个subFolder都有“subFolders”: []等等。
  2. 如果没有子文件夹,我如何让系统应对?即不在.switchMap中调用getInfoFromSubFolders
  3. 我有一种感觉,这是一种非常常见的情况,所以很可能对很多人有用。

    感激地收到任何指针

2 个答案:

答案 0 :(得分:2)

我认为expand运算符可以帮助你,因为它解决了这种用例中的递归问题。

有关详细信息,请参阅此问题:

答案 1 :(得分:0)

我突然发现这个帖子,实现了 an other problem to do with recursivity and Observables。幸运的是,我现在能够回答你的问题了。

要解决的问题 从作为参数提供的文件夹开始,您希望访问所有子文件夹。对于每个子文件夹,请访问所有子文件夹并重复此过程,不得有任何限制。

要回答有关重构和模型的问题,这个问题是典型的tree traversal,可供选择的数据结构。

您没有提供有关您的文件夹的详细信息,但让我们做一些假设。您的班级具有以下属性:

  • parent:文件夹
  • children:Folder []

您目前有一项服务可返回给定文件夹的子项。我们假设方法签名是:

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

Allez!