【问题标题】:Angular 4 how to wait for httpclient completeAngular 4如何等待httpclient完成
【发布时间】:2018-06-22 02:41:40
【问题描述】:

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

base-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);
          });
      }
}

一些数据.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 得到解决的任何建议,或者我应该怎么做才能使用我的基本服务以及使用子服务所依赖的数据来解决直到他们使用它?

【问题讨论】:

  • 是的,您可以在订阅中使用completion 回调函数,您面临的具体问题是什么。修改你的帖子它混淆了
  • @Aravind 我的问题是当我运行 getSomeData() this.apiUrlBase is undefined b/c the promise 但尚未解决。我需要一种方法来延迟这些调用,直到 this.apiUrlBase 被基础服务填充。

标签: angular


【解决方案1】:

一个快速而肮脏的方法是让 URL base 本身成为一个 observable 并确保服务中的任何内容都不能继续,直到它给出一个值:

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 之后。

【讨论】:

  • 使用初始化提供程序是否需要对从 BaseService 派生的服务进行任何更改?
  • 如果你的意思是注入它的服务,不,如果你的意思是从它继承的服务,那么我可能永远不会想到做这样的事情。相反,您可以将基础服务变成一个配置服务,该服务被注入到需要配置值的其他组件/服务中,然后您可以在应用程序范围内以相同的方式处理任何与配置相关的值,无需再经历一次。加载一次配置,然后将其注入需要的地方,干净一致且不浪费。
  • 我刚回到这个任务,想知道我应该如何修改我的 some-data.service.ts 才能使用后一种解决方案(对不起,如果这是基本的,但这是我的第一个角度 4 .net 编程 2 年后的项目)
  • 只需将其称为 ConfigService 并将其注入到其他服务中,而不是让它们继承自它,donezo。
【解决方案2】:

Async/await 可能是实现这一点的一种方式。构造函数调用异步函数“setApiUrlBase”,该函数等待“getConfigData”解析配置文件的HTTP响应,然后再设置API Url。我个人的意见是,你应该修复你的 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);
                }
            )
        });
    }
}

【讨论】:

  • 要求来自管理 b/c,这是他们总是在 .Net 应用程序的环境之间切换的方式。它实际上造成了很多麻烦,因为人为因素,但这不是我现在想打的战斗,因为我是团队中相对较新的人。相反,我试图为不同的环境引入自动构建,并随着时间的推移使“人类可读”、“人类可变”配置和手动构建过时。
  • 我明白了。这也是我的一些客户的心态。在任何情况下,如果您只需要运行一次即可获取配置,那么按照 Bryan 所说的将其设置为可注入的配置服务,或者使用诸如 localStorage 之类的存储方法来保存 url。祝你好运!
  • r 中的r = response; 是什么?我正在编译找不到名称r
  • 我想我忘了定义它。它应该是来自 json 文件的可解析响应,因此类似于 { apiUrlBase : string } 但可能是任何内容,具体取决于您的代码。最后,只需确保 getConfigData() 从主函数调用返回您需要的凭据。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-03-30
  • 2019-03-24
  • 2022-11-19
  • 1970-01-01
  • 2019-03-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多