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, i.e. loadAppConfig() returns { "apiUrl": "localhost:8080" }.
- Injected environment (AppConfigService) is defined, i.e. it's of type Object.
- environment.config is undefined, so environment.config.apiUrl returns an error: "TypeError: Cannot read properties of undefined (reading 'apiUrl')".
CodePudding user response:
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
CodePudding user response:
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.