2

I am trying to figure out how to load my config before the component gets created and APP_INITIALIZE is not working. The structure is AppModule------> HeroesModule(HeroesComponent)

code:

app.module.ts

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    HeroesModule,
    HttpClientModule
  ],
  entryComponents: [
    HeroesComponent
  ],
  providers: [],
  bootstrap: []
})
export class AppModule implements DoBootstrap {

  constructor(
    private injector: Injector
  ) {
    const customElement = createCustomElement(HeroesComponent, {injector: this.injector});
    customElements.define('heroes-component', customElement);
  }

  ngDoBootstrap() { /* Keep empty */
  }
}

heroes.module.ts

export function init_app(heroService: HeroService) {
  return () => heroService.loadConfig();
}

@NgModule({
  declarations: [HeroesComponent],
  imports: [
    CommonModule,
    HttpClientModule,
  ],
  providers: [
    { provide: APP_INITIALIZER, useFactory: init_app, deps: [HeroService], multi: true }
  ],
  exports: [
    HeroesComponent
  ]
})
export class HeroesModule { }

hero.service.ts

@Injectable({
  providedIn: 'root'
})
export class HeroService {
  private conf: any;

  constructor(private http: HttpClient) {
  }

  loadConfig() {
    const promise = this.http.get('/assets/some.json').toPromise();
    promise.then(data => {
      this.conf = data;
    });
    return promise;
  }

  get config(): string {
    if (!this.conf) {
      throw Error('error');
    }
    return this.conf.hero;
  }
}

When I try to use the HeroService in my HeroesComponent the data is not loaded. How should I use the APP_INITIALIZER in this context ?

3 Answers3

2

I had the same problem and I finally found the solution by reading this article on Medium by Netanel Basal.

All you have to do is move the creation of the custom element from the AppModule constructor to the ngDoBoostrap() function. Like this:

export class AppModule implements DoBootstrap {

  constructor(private injector: Injector) { }

  ngDoBootstrap() { 
    const customElement = createCustomElement(HeroesComponent, {injector: this.injector});
    customElements.define('heroes-component', customElement);
  }
}
onizuka87
  • 95
  • 1
  • 9
0

I think you are doing right. I just suspect loadConfig function not able to resolve promise.

Can you try modifying your loadConfig to

loadConfig() {
        return new Promise((resolve, reject) => {
            this.http
                .get('/assets/some.json')
                .map(res => res.json())  // If res already in json comment this line (Version matter)
                .subscribe(data => {
                    this.conf = data;
                    resolve(true);
                })
        })
    } 
Dipak Telangre
  • 1,792
  • 4
  • 19
  • 46
  • I tried with modified loadConfig and still the promise gets resolved after my component. also checked this answer https://stackoverflow.com/questions/49163532/angular-v5-service-is-getting-constructed-before-app-initializer-promise-resol/49418281#49418281 ; ..will try to add a stackblitz – user3218406 May 10 '20 at 22:13
0

I think there are two problems in your code.

Short answer:

  • Move the code from the AppModule constructor into the ngDoBootstrap method

Long answer:

I think only components/rendering ist delayed after the APP_INITIALIZER promises are resolved. Initialization of modules and their injected services can load before / in parallel. At least this is the behavior I see in my tests. ngDoBootstrap on the other side is called after APP_INITIALIZER promises are resolved. So everything you load in those promises should be ready to use once ngDoBootstrap is called. (I don't know the drawback of having code in that method...the comment /* Keep empty */ above seems like my answer can be improved...I see no problems yet though)

The other thing I would change is the load config method.

const promise = this.http.get('/assets/some.json').toPromise();
return promise.then(data => {
   this.conf = data;
});

In your version setting this.conf is not part of your initialization....but I guess it should be. Returning the 'then-promise' means, that app-initialization is finished only after this.conf is set.

andymel
  • 4,538
  • 2
  • 23
  • 35