Piercy
Piercy

Reputation: 809

Angular - How to make an HTTP request that has multiple subscribers / subjects

I have an angular service that calls an API and gets a list of pages. These pages are subscribed to from multiple components. With this in mind, I use BehaviorSubjects to make sure each subscriber gets the updates.

When I want to add a new page, I need to submit an HTTP POST request to the server, and handle the response. While handling it, I need to update an array, then call next() to send the updates to all of the subscribers. This requires me to have a subscriber on the HTTP call (at least, I think it does) so that I can call next(). However, while calling next, I also want the component to update and let the user know the request has been completed.

Here is a stackblitz example. In app.component.ts, line 38 has a call to the service to create a page. Below that is a section of commented out code that would subscribe and set the isSaving variable to false. However, I cannot subscribe to the method as it already has a subscription inside the pages.service.ts file.

https://stackblitz.com/edit/angular-abi4mn

I'm hoping the example makes more sense than my ramblings, apologies if the explanation is poor.

Upvotes: 2

Views: 2844

Answers (2)

Ronak Patel
Ronak Patel

Reputation: 640

If you want to update pages array on same page, use _pages observable instead of local variable page. you need to do only three changes.

<p>
  In the real life example there will be multiple subscribers to the pages array on the pages service.  When I add to the array, i need to contact the backend server as well as, update the array so the subscribers can pick up the change.  
  </p>
  <p>
  As well as updating the array, it would be nice for the component thats submitting the call to know when the save has completed.  This way i can display some visual information to the user.  In this example i use the isSaving variable.  This is the part i dont know how to handle, i want to add to the pages array, but also have the component subscribe to the http call so that it can do some stuff once the call is complete.
  </p>
  <p>

  Number of Pages: <strong>{{(pagesService._pages | async).length}}</strong> <br />  <br />
  <button (click)="addPage()" [disabled]="isSaving">Add Page</button>
</p>

set private to public to _pages variable into service.

public _pages = new BehaviorSubject<any[]>([]);

and 3rd changes in need to update isSaving variable.

public getPages() {
    this.pagesService.get().subscribe((result: any[]) => {

      this.pages = result;
this.isSaving = false;
    }, error => {

      console.error(error);
    });
  }

I have tested all scenario and it's working fine. Let me know if you have any query. Thanks.

Upvotes: 0

Mark
Mark

Reputation: 811

Currently you subscribe to the observable in your PagesService, the component which is calling the method should subscribe to it instead. So in your PagesService.add you should return the observable and you can subscribe to the observable in the calling component: this.pagesService.add(newPage).subscribe(...).

Now since you also want to do stuff in your PagesService after the POST request has succeeded, you can spy on the result of the observable without affecting it using tap operator, like so:

PagesService:

public add(page): Observable<any> {
 return this.httpClient
  .post('https://my-json-server.typicode.com/typicode/demo/posts', page)
  .pipe(
     tap(() => {
        this.pages.push(page);
        this._pages.next(this.pages);
     }),
  );
}

Upvotes: 3

Related Questions