Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Recursive using expand() and concatMap() in Angular 7

I have following API list below:

1. https://www.something.com/?filter=something

Result: 
{ 
  id: 12
  previous: null
  next: https://www.something.com/?filter=something&page=2
  data: ...
}

2. https://www.something.com/?filter=something&page=2

Result: 
{ 
  id: 13
  previous: https://www.something.com/?filter=something
  next: https://www.something.com/?filter=something&page=3
  data: ...
}

3. https://www.something.com/?filter=something&page=3

Result: 
{ 
  id: 14
  previous: https://www.something.com/?filter=something&page=2
  next: null
  data: ...
}

I want to loop through all of the urls after getting the data back of each API call, check if the next variable is null. If it is not null, it will keep continue going. while it's going through, the data of each API call will be concat and return to output.

This is what i have been trying so far

 getByURL(url: string): any {
    return this.apiService.get(url).pipe(map(data => data));
  }

  getData(): Observable<any> {
    const url = 'https://www.something.com/?filter=something';
    return this.getByURL(url).pipe(
      expand(({ data }) => {
        if (!data.next) return this.getByURL(data.next);
        else return empty();
      }),
      concatMap(({ content }) => {
        return of(content.data);
      })
    );
  }

But i got undefined result, i have struggled it for several day. Thanks

UPDATED

I put my code here in stackblitz: I have 3 api call :

http://5cf15627c936cb001450bd0a.mockapi.io/users
http://5cf15627c936cb001450bd0a.mockapi.io/users2
http://5cf15627c936cb001450bd0a.mockapi.io/users3

I want to gather all of the data after each requests. Please see the console to see the output. i always get undefined. Thanks

https://stackblitz.com/edit/angular-rai6az?file=src%2Fapp%2Fapp.component.ts

like image 488
daniel8x Avatar asked Sep 01 '25 10:09

daniel8x


1 Answers

I fixed your code as below (fixed stackblitz)

  ngOnInit() {
    this.getByPageNext()
      .subscribe(data => {
        console.log(data);
      });
  }

  get(url: string): Observable<any> {
    return this.httpClient
      .get(url)
      .pipe(catchError(this.formatErrors));
  }

  formatErrors(error: any) {
    console.error('error', error);
    return throwError(error.error);
  }

  getByURL(url: string): any {
    return this.get(url);
  }

  getByPageNext(): Observable<any> {
    const url = 'https://5cf15627c936cb001450bd0a.mockapi.io/users2';
    return this.getByURL(url)
      .pipe(
        expand((response: Array<any>) => {
          if (response && response.length && response[0].next) {
              return this.getByURL(response[0].next);
          }
          else {
            return empty()
          }
        }),
        map(obj => obj[0]), 
        reduce((acc, x: any) => acc.concat([x.data]), []),
      );
  }

so it will concat all data as array and recursive calling http works fine.

The only problem is when calling 'https://5cf15627c936cb001450bd0a.mockapi.io/users' the next url is http not https which will result in HttpError -> ProgressEvent.

So I started form 'https://5cf15627c936cb001450bd0a.mockapi.io/users2' and rest is working fine. So after you changed the backend to fix the next link, then change it back to your original request.

The other point was your result from api comes back as an array not an object, this is why I accesses result[0]

The other point was your condition to return empty() was vice versa

like image 155
Reza Avatar answered Sep 02 '25 23:09

Reza