Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get asynchronous data before Angular loads its modules

I'm trying to inject a variable into forRoot({[...]}) method of an Angular module.

I got this variable in an asynchronous way, so I tried to get it before bootstrap Angular. (It comes from cordova.js)

The problem:

It seems that Angular import the modules (and call the 'forRoot' methods) before being bootstrapped.

Is there a way to achieve this ?

Thanks !


An example of what I tried:

app.module.ts

import {NgModule} from '@angular/core';
import {AgmCoreModule} from '@agm/core';

@NgModule({
  imports: [
    AgmCoreModule.forRoot({
      apiKey: window['device'].platform, // value is 'null' instead of 'browser' or something else
      libraries: ['places']
    })
  ],
  // [...]
  bootstrap: [AppComponent]
})

export class AppModule {
}

src/main.ts

import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';

import { AppModule } from './app/app.module';
import { environment } from './environments/environment';

if (environment.production) {
  enableProdMode();
}

document.addEventListener('deviceready', () => {
  console.log(window['device'].platform); // log 'browser'
  platformBrowserDynamic().bootstrapModule(AppModule)
    .catch(err => console.log(err));
}, false);

/!\ Tips

The library I'm using (@agm/core) expect a string as apiKey, and not a function...

like image 780
Doubidou Avatar asked Nov 09 '18 15:11

Doubidou


People also ask

How does angular handle async data?

Solution 1: Use *ngIf Solution one is the easiest. Use *ngIf in blogger component to delay the initialization of posts components. We will bind the post component only if the posts variable has a value. Then, we are safe to run our grouping logic in posts component ngOnInit .

How does angular handle asynchronous call?

Basically, Async/Await works on top of Promise and allows you to write async code in a synchronous manner. It simplifies the code and makes the flow and logic more understandable. Note that because it no longer uses then and catch chaining anymore, you can handle errors by running try/catch.

What does @NgModule consolidates in angular?

NgModules consolidate components, directives, and pipes into cohesive blocks of functionality, each focused on a feature area, application business domain, workflow, or common collection of utilities. Modules can also add services to the application.

Which angular module automatically imports by BrowserModule?

BrowserModule and CommonModule link BrowserModule imports CommonModule , which contributes many common directives such as ngIf and ngFor . Additionally, BrowserModule re-exports CommonModule making all of its directives available to any module that imports BrowserModule .


1 Answers

You can use APP_INITIALIZER to provide a factory which will be executed after module imports but before Bootstrap. So you can set the apiKey to any value and override it in the factory: Create a function to fetch die needed data and set the apiKey into the LAZY_MAPS_API_CONFIG Object. The Application bootstrap will continue once all the APP_INITIALIZERS have resolved.

import { NgModule, APP_INITIALIZER } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { HttpClientModule, HttpClient } from '@angular/common/http';
import { AgmCoreModule, LAZY_MAPS_API_CONFIG, LazyMapsAPILoaderConfigLiteral } from '@agm/core';
import { AppComponent } from './app.component';
import { map } from 'rxjs/operators';

export function agmConfigFactory(http: HttpClient, config: LazyMapsAPILoaderConfigLiteral) {
  return () => http.get<{mapsApiKey: string}>("url").pipe(
    map(response => {
        config.apiKey = response.mapsApiKey;
        return response;
    })
  ).toPromise();
}

@NgModule({
  imports:      [ BrowserModule, HttpClientModule, AgmCoreModule.forRoot({ apiKey: "initialKey"}) ],
  declarations: [ AppComponent ],
  providers: [ {
    provide: APP_INITIALIZER, 
    useFactory: agmConfigFactory, 
    deps: [HttpClient, LAZY_MAPS_API_CONFIG], 
    multi: true} 
    ],
  bootstrap:    [ AppComponent ]
})
export class AppModule { }
like image 127
A.Winnen Avatar answered Nov 09 '22 09:11

A.Winnen