Как реорганизовать HttpRequests во вложенных циклах for, которые используют подписку?

у нас есть REST-API, который не зависит от внешнего интерфейса, что означает, что он всегда отправляет IRI на свои вложенные ресурсы. Таким образом, чтобы получить некоторые данные, вам всегда нужно делать несколько HTTP-вызовов (сначала получить родительский ресурс, затем его дочерние ресурсы и т. д.). Таким образом, каждая страна имеет список связанных записей. Каждая запись связана с продуктом, который имеет IRI для своего ресурса категории.

export interface Country {
  countryId: number;
  name: string;
  code: string;
  projects: string[]; //contains an array of IRIs for the project resources
  entries: string[];
}

export interface Entry {
  entryId: number,
  country: string,
  information: string,
  strategy: string,
  action: string,
  user: string,
  product: string,
  salesStatus1: string,
  salesStatus2: string,
  salesStatus3: string,
  salesStatus4: string,
  salesStatus5: string,

}

export interface Product {
  productId: number,
  productName: string,
  sharepointId: string,
  category: string,
  plmId: string,
  productStatus1: string,
  productStatus2: string,
  productStatus3: string,
  productStatus4: string,
  productStatus5: string,
  productComponents: string[]
}

export interface Category {
  categoryId: number,
  name: string,
  children: string[]
}
export class Node {
  children: Node[] = [];
  name: string;
  isProduct: boolean;
}

Итак, чтобы использовать все данные, которые мне нужны для отображения дерева навигации, я написал следующий код:

ngOnInit() {
    let nodes: Node[] = new Array<Node>();
    this.countriesService.getAll()
      .subscribe(
        (countries) => {
          for (let country of countries) {
            let countryNode = new Node();
            countryNode.name = country.name;
            countryNode.isProduct = false;
            for (let entryUrl of country.entries) {
              this.entriesService.getByUrl(entryUrl).subscribe(
                (entry) => {
                  this.productsService.getByUrl(entry.product).subscribe(
                    (product) => {
                      this.categoryService.getByUrl(product.category).subscribe(
                        (category) => {
                          let categoryNode = new Node();
                          categoryNode.name = category.name;
                          categoryNode.isProduct = true;
                          countryNode.children.push(categoryNode);
                          for (let childrenUrl of category.children) {
                            this.categoryService.getByUrl(childrenUrl).subscribe(
                              (childCategory) => {
                                let categoryChildNode = new Node();
                                categoryChildNode.name = childCategory.name;
                                categoryChildNode.isProduct = false;
                                categoryNode.children.push(categoryChildNode);
                              }
                            )
                          }
                        }
                      )
                    }
                  )
                }
              )
            }
            nodes.push(countryNode);
          }
          this.dataChange.next(nodes);
        }
      );
  }

Однако, поскольку я новичок в Angular и rxjs, у меня возникают проблемы с ожиданием завершения всех вызовов и наличия всех данных (как асинхронных) (и поэтому дерево навигации всегда пропускает элементы). Кроме того, это довольно уродливая и плохая практика связывать такие цепи. Итак, я хотел преобразовать код в rxjs-методы, однако я полностью потерял, как даже начать с него, потому что после извлечения данных мне нужно снова перебрать его, чтобы получить IRI вложенных ресурсов, а также создать объекты Node для навигация по дереву.

Не могли бы вы помочь мне, как реорганизовать этот код?


person Toxuas    schedule 10.07.2020    source источник
comment
Я считаю, что на него уже дан ответ в stackoverflow.com/questions/45161800/ и stackoverflow.com/questions/62677272/chain-observable/   -  person buchipper    schedule 10.07.2020
comment
я бы рекомендовал использовать обещание с синтаксисом async/await вместо подписки в вашем случае   -  person topmoon    schedule 10.07.2020
comment
Почему..? Все в Angular основано на Observables. Async/await — это анти-шаблон в Angular, и его действительно следует избегать. Изучайте RxJs, оно того стоит.   -  person MikeOne    schedule 10.07.2020


Ответы (1)


не уверен, что это может быть полезно, я объединил запросы с операторами rxjs.

this.countriesService.getAll()
  .pipe(
    map((cs) => forkJoin(cs.map(c => {
      const countryNode = new Node();
      countryNode.name = c.name;
      countryNode.isProduct = false;

      return forkJoin(c.entries.map(entryUrl => this.entriesService.getByUrl(entryUrl)))
        .pipe(
          switchMap(entries => forkJoin(entries.map(entry => this.productsService.getByUrl(entry.product)))),
          switchMap(products => forkJoin(products.map(product => this.categoryService.getByUrl(product.category)))),
          switchMap(categories => forkJoin(categories.map(category => {
            const categoryNode = new Node();
            categoryNode.name = category.name;
            categoryNode.isProduct = true;

            return forkJoin(category.children.map(childrenUrl => this.categoryService.getByUrl(childrenUrl)))
              .pipe(
                map(cc => {
                  categoryNode.children = cc.map(childCategory => {
                    const categoryChildNode = new Node();
                    categoryChildNode.name = childCategory.name;
                    categoryChildNode.isProduct = false;
                    return categoryChildNode;
                  });
                  return categoryNode;
                })
              );
          }))),
          map(categoryNodes => {
            countryNode.children = categoryNodes;
            return countryNode;
          })
        );
    })))
  ).subscribe(countryNodes => {
    this.dataChange.next(countryNodes)
  });
person topmoon    schedule 10.07.2020