Home > Back-end >  Passing an observable with async pipe to an input causes an infinite loop of calling http method and
Passing an observable with async pipe to an input causes an infinite loop of calling http method and

Time:09-30

I have a table where there is a column that requires a value from an HTTP request for each record, so I used a method that returns an observable as follows:

getItem(obj): Observable<string> {
        return this.http.get('/someServiceurl').pipe(
          map(res => {
            var x = res.element
            return x;
          });
      }

In the table the row is as follows (where the input is of type string):

    <td>
        <component [input]="(getItem(obj) | async)">
        </component>
    </td>

What happens is that it goes into infinite HTTP calls even without passing the value to the component input, although the value is returned from the HTTP call.
So what causes that and is there another method to call an HTTP in a loop for each element in the table asynchronously.
Thanks in advance.

Here is the whole table structure:

<p-table [value]="mainList" 
            [totalRecords]="Count" [paginator]="true" [showCurrentPageReport]="true"
            currentPageReportTemplate="Showing {first} to {last} of {totalRecords} entries" [rows]="5">
    <ng-template pTemplate="header">
        <tr>
            <th>..........</th>
            <th>..........</th>
            <th>..........</th>
        </tr>
    </ng-template>
    <ng-template pTemplate="body" let-obj>
        <tr>
            <td>{{obj.date}}</td>
            <td>{{obj.status}}</td>
            <td>
                <component [input]="(getItem(obj) | async)">
                </component>
            </td>
        </tr>
    </ng-template>
</p-table>   

CodePudding user response:

That

<component [input]="(getItem(obj) | async)"></component>

looks like the culprit (let me guess, change detection strategy: default?). On every change detection cycle the expression getItem(obj) is evaluated, http.get gets called, requests goes out, and, I'm guessing, that triggers change, which triggers change detection, and here we go again.

One possible solution: from that

http.get('/someServiceurl').pipe(map(res => res.element))

make a pipe:

@Pipe({name: 'getItem'})
export class GetItemPipe implements PipeTransform {
  constructor(private http: HttpClient) {}

  transform(value: string): Observable<SomeInterface> {
    return this.http.get(value).pipe(map(res => res.element));
  }
}

and in the template:

<component [input]="obj | getItem | async"></component>

CodePudding user response:

so yea, this is causing an infinite change detection loop. functions in template run on every change detection cycle, which causes a new observable to be constructed, which causes the async pipe to run, then output from the async pipe causes a change detection cycle. rinse, repeat.

pipe method works as that breaks the loop by ensuring the observable is only built when the input to it (obj in this case) changes. It's probably the more appropriate course if you will need to build this same observable in the same manner often, but may be a little heavy if you're only doing it here.

An alternative is to set the observable on the items in your table. something along the lines of.

// this is just a guess / example. i don't know how your mainList is actually built
this.mainList = this.service.fetchList().map(i => {
  i.item$ = this.getItem(i);
  return i;
})

then in template:

<component [input]="obj.item$ | async"></component>

this similarly ensures the observable on each item in the list is only built when the list itself is built.

Another alternative is to just handle the subscribing in the sub component, but maybe don't want to do that for other reasons.

  • Related