简体   繁体   中英

Undefined config when injecting a config service

This is a follow-up of my previous post . I've been debugging this issue for quite a while now and even though I haven't fixed it, I made some discoveries so maybe someone will be able to help.

Here's the whole setup:
app-config.json (/src/assets/):

{
  "apiUrl": "localhost:8080"
}

app-config.service.ts (/src/app/):

import {Injectable, Injector} from '@angular/core';
import {HttpClient} from '@angular/common/http';

@Injectable({
  providedIn: 'root'
})
export class AppConfigService {

  private appConfig: any;

  constructor (private injector: Injector) { }

  loadAppConfig() {
    let http = this.injector.get(HttpClient);

    return http.get('/assets/app-config.json')
      .toPromise()
      .then(data => {
        this.appConfig = data;
      })
  }

  get config() {
    return this.appConfig;
  }

}

app.module.ts (/src/app/):

import {APP_INITIALIZER, NgModule} from '@angular/core';
import {HttpClientModule} from '@angular/common/http';
import {AppConfigService} from './app-config.service';
import {CometdService} from './cometd/cometd.service';

const appInitializerFn = (appConfig: AppConfigService) => {
  return () => {
    return appConfig.loadAppConfig();
  }
};

@NgModule({
...
  providers: [HttpClientModule,
    AppConfigService,
    {
      provide: APP_INITIALIZER,
      useFactory: appInitializerFn,
      multi: true,
      deps: [AppConfigService]
    }]
})

export class AppModule {
  constructor(cometdService: CometdService) {}
}

cometd.service.ts (/src/app/cometd/):

import {Injectable, OnDestroy} from '@angular/core';
import {Store} from '@ngrx/store';
import * as fromRoot from '../reducers';
import {AppConfigService} from '../app-config.service';

export interface CometDExtended extends cometlib.CometD {
  websocketEnabled: boolean;
}

@Injectable({
  providedIn: 'root'
})
export class CometdService implements OnDestroy {

  protected cometd: CometDExtended = new cometlib.CometD() as CometDExtended;

  private subscriptions: cometlib.SubscriptionHandle[] = [];

  constructor(private environment: AppConfigService, private store: Store<fromRoot.State>) {
    let config = environment.config;
    let apiUrl = environment.config.apiUrl;
    
    this.cometd.configure('http://localhost:8080/cometd');
    this.startConnection();
  }
...
}
  • The issue happens for various services. CometD is only an example.
  • The data in app-config.service.ts itself is fetched properly, ie loadAppConfig() returns { "apiUrl": "localhost:8080" }.
  • Injected environment (AppConfigService) is defined, ie it's of type Object.
  • environment.config is undefined, so environment.config.apiUrl returns an error: "TypeError: Cannot read properties of undefined (reading 'apiUrl')".

AppConfigService is not needed in providers array because providedIn: 'root' already make it available.

If you provide the service in different ways you may have multiple instances: one will be loaded, others would not.

If it still does not work, put a breakpoint to check if other services are created before the init completion. I recommand to move the calls out of the CometdService constructor, so you can perform the async call in a clean way

Welp, just minutes after posting the question I happened to find a solution. It seems like since loadAppConfig() is asynchronous, the environment.config might've been accessed before the promise was resolved. Changing the constructor to:

this.environment.loadAppConfig().then(() => {
let config = environment.config
...
});

fixed the issue.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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