Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular2 load configuration from backend on startup

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');
    }
}
like image 301
Gamerz Sydney Avatar asked Jul 11 '16 06:07

Gamerz Sydney


2 Answers

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.

like image 167
Thierry Templier Avatar answered Nov 01 '22 09:11

Thierry Templier


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

like image 27
Dainius Lukša Avatar answered Nov 01 '22 09:11

Dainius Lukša