32

Typescript: 2.2.0 Angular: 4.0

I am attempting to ensure that a ConfigService object is initialized before application startup through the use of APP_INITIALIZER. I've found many examples of how to do this however NONE of them seem to be delaying the initialization of the app. Here are just a handful of examples I've attempted to implement.

https://github.com/angular/angular/issues/9047 https://gist.github.com/fernandohu/122e88c3bcd210bbe41c608c36306db9 Angular2 APP_INITIALIZER not consistent

Here is my NgModule class

export function init(config: ConfigService) {
  return () => {
    config.load();
  };
}


@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    FormsModule,
    HttpModule,
    AppRoutingModule
  ],
  providers: [
    {
      'provide': APP_INITIALIZER,
      'useFactory': init,
      'deps': [ConfigService],
      'multi': true
    },
    ConfigService
  ],
  bootstrap: [AppComponent]
})
export class AppModule {
}

And here is the ConfigService class

@Injectable()
export class ConfigService {
  private config: ApplicationConfiguration;

  get apiRoot() {
    return this.getProperty('apiRoot'); // <--- THIS GETS CALLED FIRST
  }

  constructor(private http: Http) {
  }

  load(): Promise<any> {
      console.log('get user called');
      const promise = this.http.get('./../../assets/config.json').map((res) => res.json()).toPromise();
      promise.then(config => {
        this.config = config;     // <--- THIS RESOLVES AFTER
        console.log(this.config);
      });
    return promise;
  }

  private getProperty(property: string): any {
    //noinspection TsLint
    if (!this.config) {
      throw new Error(`Attempted to access configuration property before configuration data was loaded, please double check that 'APP_INITIALIZER is properly implemented.`);
    }

    if (!this.config[property]) {
      throw new Error(`Required property ${property} was not defined within the configuration object. Please double check the 
      assets/config.json file`);
    }

    return this.config[property];
  }
}

And to test everything I've injected ConfigService into AppComponent with this.

import { Component } from '@angular/core';
import {ConfigService} from './services/config.service';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent {
  title = 'app works!';
  fullImagePath = '/src/image/avatar.jpeg';


  constructor(private config: ConfigService) {
    config.apiRoot;
  }
}

1 Answer 1

25

Looks like you forgot to return value from factory:

export function init(config: ConfigService) {
  return () => {
    return config.load(); // add return
  };
}

or the same code can be written a bit shortly:

export function init(config: ConfigService) {
   return () => config.load();
}
Sign up to request clarification or add additional context in comments.

8 Comments

For others reading this, theres also a more recent (as of now) juristr.com/blog/2018/01/ng-app-runtime-config/…
*typo: more recent solution/approach
There's a subtle bug in this. While the app will not bootstrap until your config loads as determined via config.load(), however your ConfigService WILL be available as a service to inject into other services long before config.load() completes. Yup, all those other services you wrote that you inject ConfigService into will be injected with an incomplete / not yet loaded object. So, you need to be very careful how the rest of your app uses the ConfigService, because you're dealing with an object that hasn't really finished constructing. Dont rely on getting lucky with injector/factory ordering.
@goat do you have any examples/advice to offer for a better approach?
@JoelDavey Vote on my feature request so the ng team gives us a solution. I requested async providers, but we'll see what they do.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.