Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Bug] Route-level Environment Initializer Skipped During AppEngine Route Discovery #29483

Open
Char2sGu opened this issue Jan 25, 2025 · 3 comments
Assignees
Labels
area: @angular/ssr needs: investigation Requires some digging to determine if action is needed

Comments

@Char2sGu
Copy link

Which @angular/* package(s) are the source of the bug?

Don't known / other

Is this a regression?

Yes

Description

In Hybrid Rendering, the AppEngine performs a route discovery run of the application to locate routes. During this run, environment initializers defined at the route level are skipped:

export const appConfig: ApplicationConfig = {
  providers: [
    provideZoneChangeDetection({ eventCoalescing: true }),
    provideRouter(routes),
    provideClientHydration(withEventReplay()),
    provideAppInitializer(() => {
      console.error('app init');
    }),
    provideEnvironmentInitializer(() => {
      console.error('root environment init');
    }),
  ],
};
export const routes: Routes = [
  {
    path: '',
    providers: [
      provideEnvironmentInitializer(() => {
        console.error('route environment init');
      }),
    ],
    loadComponent: () =>
      import('./test/test.component').then((m) => m.TestComponent),
  },
];

When running ng serve:

$ ng serve
Component HMR has been enabled.
If you encounter application reload issues, you can manually reload the page to bypass HMR and/or disable this feature with the `--no-hmr` command line option.
Please consider reporting any issues you encounter here: https://github.com/angular/angular-cli/issues

Browser bundles     
Initial chunk files  | Names            |  Raw size
polyfills.js         | polyfills        |  90.20 kB | 
main.js              | main             |  23.96 kB | 
chunk-YQOESBWP.js    | -                | 754 bytes | 
styles.css           | styles           |  95 bytes | 

                     | Initial total    | 115.00 kB

Lazy chunk files     | Names            |  Raw size
chunk-ALIS4MEI.js    | test-component   |   1.61 kB | 


Server bundles      
Initial chunk files  | Names            |  Raw size
polyfills.server.mjs | polyfills.server | 572.91 kB | 
main.server.mjs      | main.server      |  25.28 kB | 
server.mjs           | server           |   1.86 kB | 
chunk-FFILDKJJ.mjs   | -                | 788 bytes | 

Lazy chunk files     | Names            |  Raw size
chunk-ZIQDPBAX.mjs   | test-component   |   1.65 kB | 

Application bundle generation complete. [0.998 seconds]

Watch mode enabled. Watching for file changes...
NOTE: Raw file sizes do not reflect development server per-request transformations.
  ➜  Local:   http://localhost:4200/
  ➜  press h + enter to show help
root environment init
app init
root environment init
app init
route environment init

It's easy to notice the route-level environment initializer only run once: it did not run during route discovery.

Please provide a link to a minimal reproduction of the bug

https://github.com/Char2sGu/issue-missing-env-init

Please provide the exception or error you saw


Please provide the environment you discovered this bug in (run ng version)

Angular CLI: 19.1.4
Node: 22.3.0
Package Manager: npm 10.8.1
OS: linux x64

Angular: 19.1.3
... animations, common, compiler, compiler-cli, core, forms
... platform-browser, platform-browser-dynamic, platform-server
... router

Package                         Version
---------------------------------------------------------
@angular-devkit/architect       0.1901.4
@angular-devkit/build-angular   19.1.4
@angular-devkit/core            19.1.4
@angular-devkit/schematics      19.1.4
@angular/cli                    19.1.4
@angular/ssr                    19.1.4
@schematics/angular             19.1.4
rxjs                            7.8.1
typescript                      5.7.3
zone.js                         0.15.0

Anything else?

No response

@Char2sGu
Copy link
Author

Addition: such environment initializers used to work with Angular v19.0. After upgrading to Angular v19.1, they are skipped.

@JeanMeche JeanMeche transferred this issue from angular/angular Jan 26, 2025
@alan-agius4
Copy link
Collaborator

This is expected because, during route extraction, navigation to the root page no longer occurs.

What is the use case that requires these to be invoked during extraction?

@alan-agius4 alan-agius4 added needs: more info Reporter must clarify the issue area: @angular/ssr labels Jan 26, 2025
@Char2sGu
Copy link
Author

Hi @alan-agius4 thanks for the feedback. The workaround to angular/angular#51532 is to use a route level environment initializer to get the injector instance, so that loadChildren can be wrapped to run in a injection context:

/**
 * Workaround to emulate an injection context in the route's loadChildren resolver.
 * @param route
 * @returns
 * @see https://github.com/angular/angular/issues/51532#issuecomment-1956138610
 */
export function setupInjectionContextForLoadChildren(route: Route): Route {
  let injector: Injector | undefined = undefined;
  const injectorInitializerProvider: Provider = {
    provide: ENVIRONMENT_INITIALIZER,
    multi: true,
    useFactory:
      (instance = inject(Injector)) =>
      () => {
        injector = instance;
      },
  };

  const transformRoute = (child: Route) => {
    if (!child.loadChildren) return child;
    const loadChildren = child.loadChildren;
    child.loadChildren = (...args) => {
      if (!injector) throw new Error('Missing injector');
      return runInInjectionContext(injector, () => loadChildren(...args));
    };
    return child;
  };

  return {
    path: '',
    providers: [injectorInitializerProvider],
    children: [transformRoute(route)],
  };
}
export const APP_ROUTES: Routes = [
  setupInjectionContextForLoadChildren({
    path: '',
    loadChildren: async (providersLoader = inject(MyProvidersLoader)) => [
      {
        path: '',
        providers: await providersLoader.load(),
        component: MyComponent,
      },
    ],
  }),
];

@alan-agius4 alan-agius4 self-assigned this Jan 29, 2025
@alan-agius4 alan-agius4 added needs: investigation Requires some digging to determine if action is needed and removed needs: more info Reporter must clarify the issue labels Jan 29, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area: @angular/ssr needs: investigation Requires some digging to determine if action is needed
Projects
None yet
Development

No branches or pull requests

2 participants