简体   繁体   中英

Angular 5 - how to chain observables in http calls?

I am writing an app with Angular 5 and I would like to keep raw http calls in their own services so the other services can manipulate the responses as needed. For example, I will have a component, component service, and component data service.

How can I chain my observables with this pattern? Here is what I have tried:

licenseData.service.ts

import { Injectable } from '@angular/core';
import { HttpClient, HttpParams, HttpErrorResponse } from '@angular/common/http';
import { CONSTANT } from '../../environment/constants';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/map';
import 'rxjs/add/observable/throw';

@Injectable()
export class LicenseDataService {

    constructor(private http: HttpClient) {}

    getLicenseInfo() {

        const params = new HttpParams()
            .set('unsername', 'swapp')
            .set('comment', "hi");

        const endpoint = '/myendpoint';

        return this.http
            .get(endpoint, {params: params})
            .map((response) => {
                console.log(' response ', response);
                //return Observable.of<any>(response);
                return response['data'];
            })
            .catch(this.errorHandler);
    }

    errorHandler(error: HttpErrorResponse) {
        //TODO: log the error here
        return Observable.throw(error);
    }

}

license.service.ts

import { Injectable } from '@angular/core';
//import { Observable } from 'rxjs/Observable';
import { LicenseDataService } from './licenseData.service';

@Injectable()
export class LicenseService {

    constructor(private licenseDataService: LicenseDataService) { }

    getVersion() {

        //handle the error in here and the loading as well

        return this.licenseDataService.getLicenseInfo()
            .subscribe((response) => {
                return response['versionNumberField'];
            });
        //console.log(' result ', result);

        //return result['versionNumberField'];
    }

}

and here is part of my component code:

export class AboutComponent implements OnInit {

    //version: Observable<string>;
    version: string;

    constructor(private licenseService: LicenseService) {
        console.log('Hello Component');
    }

    ngOnInit() {
        console.log('ngOnInit');

        //this.version = this.licenseService.getVersion();

        this.licenseService.getVersion()
           .subscribe(response => this.version = response);
    }

}

My project won't build. How can I chain this properly?

You are can't subscribe to Subscribtion object.

You've got this code in LicenseDataService :

return this.licenseDataService.getLicenseInfo()
            .subscribe((response) => {
                return response['versionNumberField'];
            });

and in LicenseService you try to Subscribe to what LicenseDataService returns which is already a Subscribtion. One of options is using map in LicenseService and then Subscribe in Component.

  return this.licenseDataService.getLicenseInfo()
            .map((response) => {
                return response['versionNumberField'];
            });

after this your subscribe in Component should work.

I personally would stay with one service - these operations are not that complicated.

You don't want to be managing subscription inside of your service unless you absolutely have to.

If I were you I would expose the HTTP service as an observable and use a share replay so that it shares the results when you subscribe to the appVersion through the lifecycle of the app. This way you do not have an internal subscription inside of your services, which is a code smell.

    @Injectable()
    export class LicenseService {


          private _appVersion$;

          get appVersion$(){
              if(!this_appVersion$){
                this._appVersion$ = this.getVersion().pipe(shareReplay());   
              }
              return this._appVersion$;
          }

          private getVersion() {

        //handle the error in here and the loading as well

            return this.licenseDataService.getLicenseInfo()
               .pipe(
                  map(response => return response['versionNumberField'] as any
               );
         }     
   } 

Then in your component, you can inject the service and subscribe to the appVersion$ observable to get the app version value.

The reason I would do it this way It keeps everything in a more reactive programming paradigm and allows you to chain actions together with other asynchronous code. And it doesn't have the subscription inside of a service.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM