Home > OS >  Proper way to create and listen to a HTTP observable with dynamic payload
Proper way to create and listen to a HTTP observable with dynamic payload

Time:03-09

Scenario

User has a menu that create's a set of car criteria that will be used to filter out the results server side. When the user clicks the Load button, it sends the criteria set to obtain right results and then pass them to a child component for display.

I am trying to do this properly by creating a observable and while it works, it just doesn't smell right.

Parent HTML

<h3>Select Criteria</h3>
<div>
<!-- Bunch of dropdowns and radio buttons and gizmos that form the filter menu omitted for brevity-->
</div>
<button (click)="loadCars()">Load Cars</button>
<child-comp-table [dataSet]="results$ | async"><child-comp-table>

Parent TS

var _results$ = new BehaviorSubject<any[]>([]);
readonly results$ = this._results$.asObservable();

loadCars() {
    // construct payload based on selected criteria and store in postPayload variable

    this.http.post(url, postPayload).subscribe((res:any) => {
        this._results$.next(res.data);
    });
}

Is this the proper way to set this up, my concern stems from the fact that i have to subscribe as opposed to using the existing pipe.

CodePudding user response:

this.http.post(url, postPayload) returns an Observable, so i don't see why you're subscribing to it and "hold" the result inside a behavior subject.

i would go with the following direction:

let results$ = of([]); // init the obs with an observable containing empty list

loadCars() {
    this.results$ = this.http.post(url, postPayload);
}

by the way, I would also take out the HTTP call to it's own service, but that's another matter

CodePudding user response:

In similar cases, what I tend to have is the following

  • have a service class, e.g. carService, where the "call http" logic is held
  • carService exposes an API method which can be invoked to trigger the call (e.g. loadcars(postPayload))
  • carService exposes also an Observable (e.g. loadCarsResult$) as API and whoever wants to be notified with the result of the call has to subscribe to loadCarsResult$

The implementation of carService is very similar to the one you have developed, just extracted into a separate service, like this

@Injectable({
  providedIn: 'root',
})
export class carService {
  private _results$ = new BehaviorSubject<any[]>([]);
  public results$ = this._results$.asObservable();

  loadCars(postPayload) {
    // pass the _result$ Subject to the subscribe method, which ensures that
    // next, error and complete are correctly invoked
    this.http.post(url, postPayload).subscribe(this._results$);
  }
}

In this way you have created a "multicast" mechanism. In other words many components can subscribe to the same result$ Observable and all of them will be updated as soon as a result from the http call arrives.

Another way to look at the same thing is to think that you have separated the invocation of the http call from the consumption of the result returned.

  • Related