递归可观察调用中的Angular 4加载树结构 [英] Angular 4 loading tree structure in recursive observable calls

查看:47
本文介绍了递归可观察调用中的Angular 4加载树结构的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是Observables的新手,我正在寻找一种通过递归的Observable调用加载导航树的方法.导航应基于目录和子目录中的所有 index.json 文件动态构建.

Hi I'm pretty new to Observables and I'm looking for a way of loading my navigation tree with recursive observable calls. The Navigation should be build up dynamically base on all the index.jsonfiles in the directory and sub directories.

只有第一个调用的网址是静态的:/public/index.json

这是目录结构.每个目录可能包含一个 index.json ,提供有关其内容的信息,并可以通过 loadChildrenFromUrl 属性引用其他索引文件.

This is the directory structure. Each directory may contain a index.json, providing information about its content and may references to other index files via the loadChildrenFromUrl property.

|-public
   |- subdir1
       |- index.json
       |- test.html
   |- subdir2  
       |- index.json
       |- test.html
       |- subdir2.1  
           |- index.json
           |- . . .
   |- index.json

导航文件 index.json

[
  // static entry with static children
  {
    "state": "module1",
    "name": "Modul 1",
    "type": "sub",
    "icon": "dashboard",
    "children": [
       {"state": "", "name": "Index", "icon": "https" },
       {"state": "test1", "name": "Test1", "icon": "live_help"}
    ]
 },
 {
   // dynamic entry children needs to be load from url
   "state": "test",
   "name": "Test loaded from url",
   "type": "sub",
   "icon": "info_outline",
   "loadChildrenFromUrl": "subdir2/index.json"
   "children": [] // should be loaded via url
  },
  . . .
]

结果应该是一个描述整个导航树的大对象.因此,孩子可能包含孩子,孩子可能包含孩子....一个Router-Guard( CanActivate返回Observable )将小心等待直到树的加载完成.

The result should be one large object describing the whole navigation tree. So Children may contain children may contain children... . A Router-Guard (CanActivate returning Observable) will take care to wait until loading the tree has finished.

我的代码正在运行,但是该函数在加载整个树之前返回.我知道整个事情都是异步的,所以这是设计使然,但我不知道如何正确解决.看起来我必须使用flatMap?

My code is working but the function returns before the whole tree is loaded. I know the whole thing is async so this is by design but I've no idea how to solve it right. Looks like I've to use flatMap?

NavigationService.ts

NavigationService.ts

loadNavigation(): Observable<Menu[]> {
    if (this.navigationLoaded) {
      return Observable.of(this.navigationTree);
    } else {
      this.navigationTree = new Array();
      return this.loadNavigationByUrl('public', this.navigationTree);

    }
}

loadNavigationByUrl(url: string, navArray: Menu[]): Observable<Menu[]> {

    console.log(`Loading ${url}/index.json`);

    const result = this.http.get<Menu[]>(`${url}/index.json`, { responseType: 'json' });
    result.catch((err) => this.handleError(err));
    result.subscribe(data => {

      // console.log(data);
      if (data) {

        data.forEach((item: Menu, index: number, array: Menu[]) => {

          // add to navigationTree
          navArray.push(item);

          if (item.loadChildrenFromUrl && item.loadChildrenFromUrl !== '') {
            item.children = new Array();
            this.loadNavigationByUrl(`${url}/${item.loadChildrenFromUrl}`, item.children);
          }

          // console.log(this.navigationTree);
        });

        // this.navigationTree = data;
        console.log('navigation loaded');
        this.navigationLoaded = true;
      }
    },
    err => {

    },
    () => {
      console.log(`Loading ${url}/index.json completed`);
    }
    );

    return result;

}

那么如何构建可观察的链"?为此吗?

So how to construct an observable "chain?" to do this?

新信息2017年12月1日

最后,我需要在 Route Guard 中使用此功能,以便在激活路由之前加载导航结构.

At the end I need to use this function in a Route Guard so navigation structure gets loaded before route gets active.

NavigationGuard.ts

NavigationGuard.ts

@Injectable()
export class NavigationGuard implements CanActivate, CanActivateChild  {

  constructor(private svc: NavigationService, private router: Router) { }

  canActivate(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
    // console.log('canActivate');
    return this.svc.loadNavigation()
      .mapTo(true) // I'm not interested in the result
      .catch((error: any) => {
        console.log(error);
        return Observable.of(false);
      });

  }

  canActivateChild(route: ActivatedRouteSnapshot, state: RouterStateSnapshot):  Observable<boolean> | Promise<boolean> | boolean {
    return this.canActivate(route, state);
  }
}

推荐答案

不考虑为什么?"因为我对递归可观察结构感兴趣...您无法通过嵌套订阅的可观察对象进行递归.您需要使用更高阶的可观测值,并且您实际上绝对不应该订阅.关键在于呼叫者需要订阅,否则它将永远无法工作.

Leaving aside the"WHY??" because I'm interested in a recursive observable structure... You can't recurse through an observable with nested subscriptions. You need to use higher order observables, and you should really never subscribe at all. the key to this is that the caller needs to subscribe, otherwise it will never work.

loadNavigation(): Observable<Menu[]> {
    if (this.navigationLoaded) {
      return Observable.of(this.navigationTree);
    } else {
      let navigationTree = new Array();
      return this.loadNavigationByUrl('public', this.navigationTree)
                 .do(data => {
                   // console.log(data);
                   if (data) {

                     this.navigationTree = data;
                     console.log('navigation loaded');
                     this.navigationLoaded = true;
                   }
                  }); // could subscribe here instead if you really want.
    }
}

loadNavigationByUrl(url: string, navArray: Menu[]): Observable<Menu[]> {

  console.log(`Loading ${url}/index.json`);

  return this.http.get<Menu[]>(`${url}/index.json`, { responseType: 'json' })
    .catch((err) => this.handleError(err))
    .switchMap(data => {
      if (!data) 
        return Observable.of(null);

      let children$ = [];
      data.forEach((item: Menu, index: number, array: Menu[]) => {
        // add to navigationTree
        navArray.push(item);

        if (item.loadChildrenFromUrl) { // FYI empty string is "false" in JS
          item.children = new Array();
          children$.push(this.loadNavigationByUrl(`${url}/${item.loadChildrenFromUrl}`, item.children));
        }
      });
      return (children$.length) ? Observable.forkJoin(children$) : Observable.of([]);
    });
}

这篇关于递归可观察调用中的Angular 4加载树结构的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆