2017-11-10 150 views
1

在我的应用程序中,我有从自定义基本服务继承的服务(即调用其余服务),这些服务定义了在所有其他服务中使用的常用方法和属性。其中一个参数是我的休息服务的基础URL,它在环境下使用了一个变量。但是现在需要使这个URL来自配置文件。所以我将不得不使用httpclient,它提出了一个问题,现在在实际服务尝试使用基本url时,它尚未解决。有没有办法如何等待,直到这个电话解决? 我的代码如下所示:Angular 4如何等待httpclient完成

基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得到解决,或者我该怎么做才能使用我的基本服务,并使用子服务依赖的数据解决,直到他们使用它?

+0

是的,你可以使用订阅里面的'completion'回调函数,你面对什么确切的问题。修改你的帖子它会混淆 – Aravind

+0

@Aravind我的问题是当我运行getSomeData()时this.apiUrlBase是未定义的B/C承诺尚未解决。我需要一种方法来延迟这些调用,直到this.apiUrlBase被基础服务填充。 – AlexanderM

回答

2

一个快速和肮脏是使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之后。

+0

与初始化提供程序是否需要对从BaseService派生的服务进行任何更改? – AlexanderM

+0

如果你的意思是,注入它的服务,不,如果你指的是从它继承的服务,那么很可能,我永远不会想到做这样的事情。您可以改为将基本服务配置到注入到需要配置值的其他组件/服务的配置服务中,然后您可以使用与应用程序相同的方式处理任何配置相关的值,无需再次进行此操作。一次加载配置,然后将其注入需要的地方,干净而一致,不浪费。 – bryan60

+0

我刚回到这个任务,想知道我应该如何修改我的some-data.service.ts以便能够使用后一种解决方案(对不起,如果这是基本的,但这是我在2年后的第一个角度4项目.net只有编程) – AlexanderM

1

异步/等待可能是实现此目的的一种方法。构造函数调用异步函数“setApiUrlBase”,它在设置API Url之前等待“getConfigData”解析配置文件的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 => { 
      this._http.get('../../assets/config.json').subscribe(
       response => { 
        r = response; 
       }, 
       error => { 
        // Config error handling 
       }, 
       () => { 
        resolve(r); 
       } 
      ) 
     }); 
    } 
} 
+0

需求来自管理B/C这是他们总是在.Net应用程序的环境之间切换的方式。它实际上造成了很多人为因素的b/c,但这不是我现在想要战斗的战斗,b/c是团队中比较新的人选。相反,我正在尝试为不同的环境引入自动构建,并随着时间的推移使“人类可读”,“人为可变”配置和手动构建过时。 – AlexanderM

+0

我明白。这也是我的一些客户的心态。在任何情况下,如果你只需要运行一次就可以获得配置,那么按照Bryan的说法制作一个可注入的配置服务,或者使用localStorage之类的存储方法来保存url。祝你好运! –