0

Consider the typical HttpClient example

In Service

  config: string[];

  load(): Observable<string[]> {
     return this.http.get<string[]>('./assets/config.json');
  }

  getConfig(key: string): string {
     if (!this.config) {
        this.load.subscribe(data => this.config = data);
     }
     return this.config[key];
  }

In Component

 value = myService.getConfig('blah');

Obviously, this example doesn't work because return in getConfig() happens before config is getting populated in subscribe().

All examples show subscribe() in the calling component. But how do I await the return inside the service?

NOTE This is just MCVE. In reality, the use case is much more complex and is not how to get the values before application starts. For example, I need to get a large array from the server, and in the service filter based on some criteria and return to component.

Felix
  • 9,248
  • 10
  • 57
  • 89
  • You can use async and await, but actually i would suggest the following: if you will call the config api only once and then state the response in your service, you should do it on the service constructor. – Nour Feb 04 '18 at 09:44
  • sorry - I should have mentioned: I tried in constructor, but the result is the same... Looks like the service isn't instantiated until it is injected in the component and even though there is `subscribe()` in the constructor, my `getConfig()` still returns undefined or empty array... – Felix Feb 04 '18 at 09:50
  • Yes you are right, the services is not instantiated until it's first inject, and sorry for the below comment but you do not forget this.config right ? if(!config) => if (!this.config) and so the return value "return this.config[key]" – Nour Feb 04 '18 at 09:54
  • yes, the typo is in the question; not in my code :) (I fixed the question) – Felix Feb 04 '18 at 09:58
  • Possible duplicate of [Angularjs2 - preload server configuration before the application starts](https://stackoverflow.com/questions/39033835/angularjs2-preload-server-configuration-before-the-application-starts) – Estus Flask Feb 04 '18 at 10:23

2 Answers2

1

Please try:

your service:

async getConfig(key: string): Promise<number> {
  if (!this.config) {
     const response = await this.load().toPromise();
     this.config = response.json();
  }
  return this.config[key];
}

your component:

async ngOnInit() {
  let config = await this.yourService.getConfig('blah');
}
Nour
  • 5,609
  • 3
  • 21
  • 24
  • I think we are close... However, I am getting `TS2339: Property 'json' does not exist on type 'Subscription'`. and I see in debugger that `return` still happens before `subscribe()` – Felix Feb 04 '18 at 17:18
  • I have updated my answer, would you give a try and update me please. – Nour Feb 05 '18 at 06:52
  • `.json()` still doesn't work - but it's not important! It works the way I expected all along: `await` actually `await`s. That is, it doesn't continue until `toPromise()` completes. And `response` in your example would be a number (in mine it's an array of key:value objects. I am not sure what I was doing wrong before but I can't reproduce the problem any more! – Felix Feb 07 '18 at 06:42
  • Glad to help :). – Nour Feb 07 '18 at 08:26
0

In this case, its recommended to use resolvers https://angular.io/api/router/Resolve

you can customize this and no need to import route args

    @Injectable()
class TeamResolver implements Resolve<Team> {
  constructor(private backend: Backend) {}

  resolve(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot
  ): Observable<any>|Promise<any>|any {
    return this.backend.fetchTeam(route.params.id);
  }
}

@NgModule({
  imports: [
    RouterModule.forRoot([
      {
        path: 'team/:id',
        component: TeamCmp,
        resolve: {
          team: TeamResolver
        }
      }
    ])
  ],
  providers: [TeamResolver]
})

From angular.io

rijin
  • 1,709
  • 12
  • 21