Reputation: 141
In my Angular2 app, I'm trying to load configurations from backend on bootstrap using HTTP so that I can use the extracted data for creating angular services.
Whenever I try to read the HTTP response saved in my Config, I always get TypeError: Cannot read property 'url' of undefined error.
It appears like the HTTP request completes only when the whole bootstrap method finishes, whereas my code tries to extract the Observable response before it is retrieved.
How can I fix it to extract configuration from server and use it to create angular services on startup? (I want to create services inside providers with extracted data)
Please comment if there is any better way of extracting configuration from server on startup.
My main.ts looks like this:
bootstrap(AppComponent,
[APP_ROUTER_PROVIDERS, HTTP_PROVIDERS, ConfigurationService, Config])
.catch(err => console.error(err)
);
configuration.service.ts
@Injectable()
export class ConfigurationService {
constructor(private http: Http) {
}
load() {
return this.http.get('config/getConfig').map(response => response.json());
}
}
config.ts
@Injectable()
export class Config {
public config;
constructor(public configurationService: ConfigurationService) {
configurationService.load().subscribe(
config => this.config = config,
error => console.error('Error: ' + error)
);
}
get(key: any) {
return this.config[key];
}
}
app.component.ts
@Component({
selector: 'app',
templateUrl: 'app/app.component.html',
styleUrls: ['app/app.component.css'],
directives: [ROUTER_DIRECTIVES],
providers: [MyService]
})
export class AppComponent {
constructor(public myService: MyService) {
}
}
my.service.ts
@Injectable()
export class MyService{
private url;
constructor(public config: Config) {
this.url = config.get('url');
}
}
Upvotes: 14
Views: 10475
Reputation: 1931
This is a better way to write the load function
public load() {
let promise =this.http.get(url).map(res => res.json()).toPromise();
promise.then(config => this.validation = config);
return promise;
};
Upvotes: -1
Reputation: 1036
When using AoT compilation it throws an error because the factory is an anonymous function. What you need to do then is export a function for the factory
export function ConfigLoader(configService: ConfigService) {
return () => configService.load();
}
and the configuration for the app looks like this:
@NgModule({
declarations: [
AppComponent
],
imports: [
HttpModule,
BrowserModule
],
providers: [
ConfigService,
{
provide: APP_INITIALIZER,
useFactory: ConfigLoader,
deps: [ConfigService]
}],
bootstrap: [ AppComponent ]
})
export class AppModule {
}
reference https://github.com/angular/angular/issues/10789
Upvotes: 10
Reputation: 202196
You could leverage the APP_INITIALIZER
service to reload the configuration before application starts:
bootstrap(AppComponent, [
{
provide: APP_INITIALIZER,
useFactory: (config:Config) => {
return config.load();
},
dependency: [ Config ]
}
]);
For this, you need to adapt a bit your ConfigService
class:
@Injectable()
export class ConfigService {
constructor(private http:Http) {}
load() { // <------
return new Promise((resolve) => {
this.http.get(...).map(res=>res.json())
.subscribe(config => {
this.config = config;
resolve();
});
}
}
Then you'll be able to access directly properties on the configuration object within your application.
Upvotes: 12