Angular 4如何等待httpclient完成

时间:2017-11-10 20:11:49

标签: angular

在我的应用程序中,我有从自定义基本服务继承的服务(正在调用其余服务),该服务定义了在所有其他服务中使用的公共方法和属性。其中一个参数是我的休息服务的基本网址,它在环境下使用变量。但是现在需要使这个url来自配置文件。因此,我必须使用httpclient来获取它,这提出了一个问题,现在当实际服务尝试使用基本URL时,它还没有解决。有没有办法等待电话结算? 我的代码如下:

基service.service.ts:

export class BaseService{
    protected apiUrlBase: string;

    constructor(protected _http: HttpClient) {
        // some logic

        this.getConfigData()
          .subscribe(
            x => {
              this.apiUrlBase = x.apiUrlBase;
            }
          );

    }

    getConfigData(): Observable<any> {
        return this._http.get(
          '../../assets/config.json'
        )
          .do(x => {
          })
          .catch(error => {
            alert('Unable to read configuration file:' + JSON.stringify(error));
            return Observable.throw(error);
          });
      }
}

一些-data.service.ts:

@Injectable()
export class TestService extends BaseApiService {
    private get _someDataServiceUrl(): string {
        return this.apiUrlBase + '/api/v1/somedata';
    }

    constructor(_http: HttpClient) {
        super(_http);
    }

    getSomeData(): Observable<ISomeData[]> {
        return this._http.get<ISomeData[]>(this._someDataServiceUrl)
          .do(this.handleSuccessResponse)
          .catch(this.handleErrorResponse);
    }
}

有关我如何等待我的服务直到apiUrl得到解决的任何建议,或者我应该怎样做才能使用我的基本服务以及使用子服务所依赖的数据来解决,直到他们使用它为止?

2 个答案:

答案 0 :(得分:4)

快速而肮脏的是使URL基础本身是一个可观察的,并确保服务中的任何内容都不能继续,直到它给出一个值:

export class BaseService{
protected apiUrlBase: BehaviorSubject<string> = new BehaviorSubject<string>(null);
protected apiUrlBase$: Observable<string> = this.apiUrlBase.filter(b => !!b).take(1);

constructor(protected _http: HttpClient) {
    // some logic

    this.getConfigData()
      .subscribe(
        x => {
          this.apiUrlBase.next(x.apiUrlBase);
        }
      );

}

getConfigData(): Observable<any> {
    return this._http.get(
      '../../assets/config.json'
    )
      .do(x => {
      })
      .catch(error => {
        alert('Unable to read configuration file:' + JSON.stringify(error));
        return Observable.throw(error);
      });
  }
} 


@Injectable()
export class TestService extends BaseApiService {
  private _someDataServiceUrl(apiUrlBase): string {
      return apiUrlBase + '/api/v1/somedata';
  }

  constructor(_http: HttpClient) {
      super(_http);
  }

  getSomeData(): Observable<ISomeData[]> {
      return this.apiUrlBase$.switchMap(apiUrlBase => this._http.get<ISomeData[]>(this._someDataServiceUrl(apiUrlBase))
        .do(this.handleSuccessResponse)
        .catch(this.handleErrorResponse));
  }
}

但这显然非常难看。更好的解决方案是通过为应用程序创建应用程序初始化程序提供程序,确保此基本服务在应用程序中的任何其他内容之前加载和解析:

import { Injectable, APP_INITIALIZER } from '@angular/core';

@Injectable()
export class BaseService{
  protected apiUrlBase: string;

  constructor(protected _http: HttpClient) {
  }

  getConfigData(): Observable<any> {
    return this._http.get(
      '../../assets/config.json'
    )
      .do(x => {
      })
      .catch(error => {
        alert('Unable to read configuration file:' + JSON.stringify(error));
        return Observable.throw(error);
      });
  }

  public load() {
     return new Promise((resolve, reject) => {
       this.getConfigData()
         .subscribe(
           x => {
             this.apiUrlBase = x.apiUrlBase;
             resolve(true);
           },
           err => resolve(err)
         );
     });
  }
}

export function BaseServiceInitFactory(baseService: BaseService) {
  return () => baseService.load();
}

export const BaseServiceInitProvider = {
  provide: APP_INITIALIZER,
  useFactory: BaseServiceInitFactory,
  deps: [BaseService],
  multi: true
}

然后将BaseServiceInitProvider放在您的应用程序模块提供程序中的BaseService之后。

答案 1 :(得分:2)

Async / await可能是实现此目的的一种方法。构造函数调用异步函数&#34; setApiUrlBase&#34;,它等待&#34; getConfigData&#34;在设置API Url之前解析配置文件的HTTP响应。我个人的意见是你应该修复你的REST端点,这样你就不需要每次都有两个HTTP请求,但这取决于你想要如何实现它。

export class BaseService {
    protected apiUrlBase: string;

    constructor(protected _http: HttpClient) {
        this.setApiUrlBase();
    }

    async setApiUrlBase() {
        await this.getConfigData().then(
            response => {
                // Promise successful
                response.subscribe(
                    x => {
                        // Subscription successful
                        this.apiUrlBase = x.apiUrlBase;
                    }
                )
            },
            err => { 
                // Promise fail error handling
            }
        );
    }

    getConfigData(): Promise<Observable<Response>> {
        return new Promise(resolve => {
            let r : any;
            this._http.get('../../assets/config.json').subscribe(
                response => {
                    r = response;
                },
                error => {
                    // Config error handling
                },
                () => {
                    resolve(r);
                }
            )
        });
    }
}