Search code examples
javascriptangulartypescript

In Angular why does app.module.ts run before the completion of main.ts?


I need variables to set in my main.ts file before my app.module.ts runs. Is there any reason why this is not the default order? My understanding was that main.ts runs first in Angular and then bootstraps the AppModule.

Example of my situation.

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';
import { environmentLoader as environmentLoaderPromise } from './environments/environmentLoader';

environmentLoaderPromise.then(env => {
  if (env.production) {
      enableProdMode();
  }

  environment.hosturl = env.hosturl;
  console.log('1')
  platformBrowserDynamic().bootstrapModule(AppModule);
  console.log('2')
})

Then at the top of my app.module.ts file I have a console.log after all the imports to console.log('3').

The console results in:

3 1 2

How can I make sure that my main.ts is done updating my env var before running the app.module.ts?

app.module.ts below

import { IssuerService } from './issuer.service';
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
import { PagesModule } from './pages/pages.module';
import { CustomCompModule } from './customcomp/customcomp.module';
import { ReactiveFormsModule } from '@angular/forms';
import { AppRoutingModule } from './/app-routing.module';
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
import { MatButtonModule } from '@angular/material/button';
import { MatCheckboxModule } from '@angular/material/checkbox';
import {MatTabsModule} from '@angular/material/tabs';
import { LoginComponent } from './pages/login/login.component';
import { LayoutComponent } from './pages/layout/layout.component';

import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
import { AuthInterceptor } from './http.interceptor';

import {
  MatIconModule,
} from '@angular/material/icon';

import { MsalModule, MsalRedirectComponent, MsalGuard, MsalInterceptor } from '@azure/msal-angular'; // Import MsalInterceptor
import { InteractionType, PublicClientApplication } from '@azure/msal-browser';

const isIE = window.navigator.userAgent.indexOf('MSIE ') > -1 || window.navigator.userAgent.indexOf('Trident/') > -1;
import { MatToolbarModule } from '@angular/material/toolbar';

// Import env vars
import { environment } from "./../environments/environment";

console.log('3')
console.log('Var that isnt loaded   ' + environment.redirecturi)

@NgModule({
  declarations: [
    AppComponent,
    LoginComponent,
    LayoutComponent,
  ],
  imports: [
    // REMOVED in Angular 15 1/4/23 FlexLayoutModule,
    // MSAL AmplifyAuthenticatorModule,
    BrowserModule,
    PagesModule,
    CustomCompModule,
    ReactiveFormsModule,
    NgbModule,
    AppRoutingModule,
    BrowserAnimationsModule,
    MatButtonModule,
    MatCheckboxModule,
    MatTabsModule,
    MatIconModule,
    HttpClientModule,
    MatToolbarModule,
    MsalModule.forRoot( new PublicClientApplication({
      auth: {
        clientId: environment.clientid,
        authority: 'https://login.microsoftonline.com/' + environment.tenantid,
        redirectUri: environment.redirecturi
      },
      cache: {
        cacheLocation: 'localStorage',
        storeAuthStateInCookie: isIE,
      }
    }), {
      interactionType: InteractionType.Redirect,
      authRequest: {
        scopes: ['user.read']
        }
    }, {
      interactionType: InteractionType.Redirect, // MSAL Interceptor Configuration
      protectedResourceMap: new Map([ 
          [environment.scopeserver, [environment.scopeapi]]
      ])
    })
  ],
  providers: [    
    {
    provide: HTTP_INTERCEPTORS,
    useClass: MsalInterceptor,
    multi: true
    },
    {
      provide: HTTP_INTERCEPTORS,
      useClass: AuthInterceptor,
      multi: true
    },
    MsalGuard,
    IssuerService
  ],
  bootstrap: [AppComponent, MsalRedirectComponent] // MsalRedirectComponent bootstrapped here
})


export class AppModule {

 }


Solution

  • It's running in the correct order, because your console.log inside the app.module.ts is in the outer file scope, so once you import it, the console logs will run. Imagine the following sequence (the console output is on comments)

    import { enableProdMode } from '@angular/core';
    import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
    
    import { AppModule } from './app/app.module';
    // 3
    import { environment } from './environments/environment';
    import { environmentLoader as environmentLoaderPromise } from './environments/environmentLoader';
    
    environmentLoaderPromise.then(env => {
      if (env.production) {
          enableProdMode();
      }
    
      environment.hosturl = env.hosturl;
      console.log('1')
      // 1
      platformBrowserDynamic().bootstrapModule(AppModule);
      console.log('2')
      // 2
    })