简体   繁体   English

Angular 4如何等待httpclient完成

[英]Angular 4 how to wait for httpclient complete

In my application I have services (that are making calls to the rest service) that are inherited from the custom base service that is defining common methods and properties that are used across all other services. 在我的应用程序中,我有从自定义基本服务继承的服务(正在调用其余服务),该服务定义了在所有其他服务中使用的公共方法和属性。 One of such parameters was base url for my rest service which was using a variable under environment. 其中一个参数是我的休息服务的基本网址,它在环境下使用变量。 However now came in requirement that makes this url coming from the config file. 但是现在需要使这个url来自配置文件。 So I would have to get it using httpclient, which presents an issue that now at the time when actual service is trying to use base url it is not yet resolved. 因此,我必须使用httpclient来获取它,这提出了一个问题,现在当实际服务尝试使用基本URL时,它还没有解决。 Is there a way how to wait till that call resolves? 有没有办法等待电话结算? My code looks like: 我的代码看起来像:

base-service.service.ts: 基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);
          });
      }
}

some-data.service.ts: 一些-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);
    }
}

Any suggestions on how I can wait in my service till apiUrl is getting resolved, or what should I do to be able to use my baseservice as well as use the data that child services are relying on to be resolved till they use it? 有关我如何等待我的服务直到apiUrl得到解决的任何建议,或者我应该怎样做才能使用我的baseservice以及使用子服务所依赖的数据直到他们使用它?

A quick and dirty is to make the URL base an observable itself and make sure nothing in the service can continue until it gives a value: 快速而肮脏的是使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));
  }
}

But this is clearly very ugly. 但这显然非常难看。 A better solution would be to make sure this base service loads and resolves before anything else in the application by creating an app initializer provider for it: 更好的解决方案是通过为应用程序创建应用程序初始化程序提供程序,确保此基本服务在应用程序中的任何其他内容之前加载和解析:

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
}

then put that BaseServiceInitProvider right after your BaseService in your app module providers. 然后将BaseServiceInitProvider放在您的应用程序模块提供程序中的BaseService之后。

Async/await may be one way to implement this. Async / await可能是实现此目的的一种方法。 The constructor calls the async function "setApiUrlBase", which waits for "getConfigData" to resolve the HTTP response of config file before setting the API Url. 构造函数调用异步函数“setApiUrlBase”,它在设置API Url之前等待“getConfigData”解析配置文件的HTTP响应。 My personal opinion is that you should make your REST endpoint fixed so you do not need to make two HTTP requests everytime when one is sufficient, but it depends on how you want to implement it. 我个人的意见是你应该修复你的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);
                }
            )
        });
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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