3

My problem is very similar to this one.

In foo.component.html:

<ng-container *ngFor="let item of items">
    <ng-container *ngIf="(fooFunc(item.property) | async) as element">
        {{ element | json }}
    </ng-container>
</ng-container>

In foo.component.ts:

fooFunc(foo: any) {
  return this.fooService.fooServiceFunc(foo).pipe(
    take(1),
    shareReplay(1)
  );
}

The fooServiceFunc in fooService will return only one Observable at one time.

My problem is that now my app fires infinite requests (after the whole items array has been iterated, it will fire the request again from beginning, over and over), which seems to be a side-effect of async pipe which is announced in this answer. But I still cannot figure out how to fix this?

funkid
  • 577
  • 1
  • 10
  • 30

1 Answers1

3

Save you shared stream to variable and use the variable in template

data$ = forkJoin(
  this.items.map(item => this.fooService.fooServiceFunc(item.property).pipe(
    map(fetchResult => ({ fetchResult, item })
  ))
)
<ng-container *ngFor="let item of data$ | async">
    <ng-container *ngIf="item.fetchResults">
        {{ item.fetchResults | json }}
    </ng-container>
</ng-container>

Now you create new stream for each item, and each query runs change detection, whitch runs queries again.

My advice: Try to avoid function calls in templates, functions in template executes when changeDetection for current component runs (AsyncPipe run change detection by each value in the input stream).

izmaylovdev
  • 1,828
  • 8
  • 16
  • Sorry for my English... I can give you relevant articles if you need more details. – izmaylovdev May 17 '19 at 14:39
  • Very appericiate for your answer! I think it will be nice for you to provide the reference link since others who see this question may need this. And I found that, since the `map` syntax used in your answer, the `data$` has already iterate the whole `items` and becomes an array, too. So, there is no need to use `*ngFor="let item of items"` in html template, which means the code in html should be things like this: ``` {{ foo | json }} ``` – funkid May 17 '19 at 15:42
  • yes you're right, I updated answer with your corrections. – izmaylovdev May 17 '19 at 15:48
  • Thanks! By the way, would you please give out the reference links? I do want to know more details. – funkid May 17 '19 at 15:51
  • 1
    for understand why fetch inside of template triggers infinity loop, first at all you need to understand how change detection works, you can read this article for that: https://blog.angular-university.io/how-does-angular-2-change-detection-really-work/ – izmaylovdev May 17 '19 at 15:52
  • Thanks, I'll read this. You said that "avoid function calls in templates", now, imho, seems that there is no way to use function calls with `async` pipe in templates without causing this "infinite loop" problem. Binding the function to a varaible seems to be the only way to fix this. – funkid May 17 '19 at 15:56
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/193531/discussion-between-izmaylovdev-and-funkid). – izmaylovdev May 17 '19 at 15:57