Angular2 load configuration from backend on startup

Gamerz Sydney picture Gamerz Sydney · Jul 11, 2016 · Viewed 9.5k times · Source

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');
    }
}

Answer

Thierry Templier picture Thierry Templier · Jul 11, 2016

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.