Search code examples
angulartypescripthttpionic-frameworksubscribe

Angular subscribe loop in subscribe


I am looking to retrieve documents for each city.

I have a first http GET retrieving cities by parsing the page and then I need to make an other request for each city to retrieve associated documents.

What I have done

this.httpService.callUrl(ifrUrl).subscribe((html: string) => {
const root = parse(html);
const nodes = root.querySelectorAll(IFR_AIRPORTS_SELECTOR);
const airports = [];
for (const node of nodes) { // Loop for each city
  if (node.childNodes.length === 2) {
    const linkElement = node.childNodes[1];
    // @ts-ignore
    const link = linkElement.attributes.href;
    const airportUrl = this.urlService.getIfrUrlWithEnd(link);
    const airportName = linkElement.innerText.trim().replace('\n', ' ');
    // @ts-ignore
    const icaoSplit = linkElement.attributes.id.toString().split('.');
    const icao = icaoSplit[icaoSplit.length - 1];
    // Retrieve documents associated to the actual airport
    // with an other http request
    this.getIfrChartsForUrl(airportUrl).subscribe((charts) => {
      const airport = new IfrAirport();
      airport.icao = icao;
      airport.name = airportName;
      airport.url = airportUrl;
      airport.charts = charts;
    });
  }
}
console.log('Airports');
console.log(airports);

I am not really familiar with asynchrone functions. Here I try afterward to display the airport but the log is display directly after running the script with empty airport array...

The array build airports should be returned.

What am I missing?

T H A N K S


Solution

  • Instead of nested subscriptions and subscriptions in a loop you could use RxJS higher order mapping operator like switchMap to map an observable and forkJoin function to combine multiple observables.

    Try the following

    this.httpService.callUrl(ifrUrl).pipe(
      switchMap((html: string) => {
        const root = parse(html);
        const nodes = root.querySelectorAll(IFR_AIRPORTS_SELECTOR);
        const requests = [];
        for (const node of nodes) { // Loop for each city
          if (node.childNodes.length === 2) {
            const linkElement = node.childNodes[1];
            // @ts-ignore
            const link = linkElement.attributes.href;
            const airportUrl = this.urlService.getIfrUrlWithEnd(link);
            const airportName = linkElement.innerText.trim().replace('\n', ' ');
            // @ts-ignore
            const icaoSplit = linkElement.attributes.id.toString().split('.');
            const icao = icaoSplit[icaoSplit.length - 1];
    
            requests.push(
              this.getIfrChartsForUrl(airportUrl).pipe(
                map(charts => {
                  const airport = new IfrAirport();
                  airport.icao = icao;
                  airport.name = airportName;
                  airport.url = airportUrl;
                  airport.charts = charts;
    
                  return airport;
                })
              )
            );
          }
        }
    
        return forkJoin(requests);
      })
    ).subscribe(
      airports => console.log(airports),
      err => console.log(err)
    );