5

I am expecting only one http request but in my console I am getting multiple http calls. I am not entirely sure the reason. Below is a abbreviated for ease of readability.

component.html

{{ (user | async).firstname }}  {{ (user | async).firstname }}

<ul>
  <li *ngFor="let profile of (user | async)?.profiles "> 
    <div>
        <p>{{ profile.profileType }}<span *ngIf="isStudent(profile.specialized)"> - {{ profile.specialized }}</span></p>
        <p>{{ profile.id }}</p>
    </div>

    <button class="btn btn-primary float-right" (click)="onGo(profile)">Go</button>
  </li>
</ul>

component.ts

private user: Observable<User>;

ngOnInit(){

   this.user = this.userDataService.fetchUserDetails(+params['id']);

}

UserDataService.ts

fetchUserDetails(id:number):Observable<User> {
console.log("calls 1 ?"); // this only gets called once!!!
return this.httpClient.get<User>(this.apiUrl + "/" + id)
  .pipe(
    tap((response) => {

      console.log(response); // this is executed multiple times!!!


      return response;
    }),
    catchError( (error) => {
      handleIt();
    })
  )

}

In my console

enter image description here

In my network

enter image description here

What is making HttpClient to make so many http requests? when the UserDataService clearly only been executed once...

2
  • Is there a reason you are using map? Commented Jun 22, 2018 at 8:37
  • no reason...sorry, I think it should be tab() not map() . its just a way to console log during request Commented Jun 22, 2018 at 8:42

3 Answers 3

3

Every async pipe creates its own subscription to the observable, which ends in seperate API calls. You've got two options to solve it.

Option 1: Work with the as operator to save the result like this:

<ng-container *ngIf="user | async as u">
 {{ u.firstname }}
 ...
</ng-container>

Option 2: Work with the share operator from rxjs:

return this.httpClient.get<User>(this.apiUrl + "/" + id)   .pipe(
  tap(console.log), // this is executed multiple times!!!
    share(),
    catchError( (error) => {
      handleIt();
    })
);
Sign up to request clarification or add additional context in comments.

1 Comment

share operator doesn't work am I importing it correctly? import { share } from 'rxjs/operators';
1

More use of async pipe will affect the efficiency because it will subscribe to every async pipe. You can notice this more if you are calling an HTTP service because it will call the HTTP request for each async pipe.

{{ user.firstname }}  {{ user.firstname }}

<ul>
  <li *ngFor="let profile of user?.profiles "> 
    <div>
        <p>{{ profile.profileType }}<span *ngIf="isStudent(profile.specialized)"> - {{ profile.specialized }}</span></p>
        <p>{{ profile.id }}</p>
    </div>

    <button class="btn btn-primary float-right" (click)="onGo(profile)">Go</button>
  </li>
</ul>


ngOnInit() {
    this.userDataService.fetchUserDetails(+params['id']).subscribe(data => this.user = data);
}

1 Comment

Don't forget to unsubscribe, otherwise a memory leak is possible!
1

You use the async pipe :

{{ (user | async).firstname }}  {{ (user | async).firstname }}

This means that Angular runs the call everytime it performs a change detection, opposed to an ngOninit function which is ran only once.

If you want to cancel that, you can use distinctUntilChanged() like this :

return this.httpClient.get<User>(this.apiUrl + "/" + id)
  .pipe(distinctUntilChanged())
  .map(...)

2 Comments

shouldn't the map be inside the pipe? thought that's the rxjs6-way
I didn't use the 6th version yet, and I'm used to use it outside of the pipe. But either way, it will work.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.