Skip to content

Router initializer never completes if initial navigation fails #44355

@znikola

Description

@znikola

Which @angular/* package(s) are relevant/releated to the feature request?

router

Description

Rendering an app in SSR (using nguniversal) with an invalid URL (e.g. http://localhost:4200/electronics-spa/en/USD/Brands/Canon/c/brand_10%20or%20(1,2)=(select*from(select%20name_const(CHAR(82,88,106,99,113,78,74,70,73,118,87),1),name_const(CHAR(82,88,106,99,113,78,74,70,73,118,87),1))a)%20--%20and%201%3D1) causes the render to hang, and never release the taken resources.

The reason is that the navigation never completes on error, when the Router option initialNavigation is set to enabled.

Proposed solution

Expose RouterInitializer.
EDIT: or make the router complete the navigation on error, if that makes sense.

Alternatives considered

We came up with this workaround, but it would be nice if ɵangular_packages_router_router_h could be exposed.

import { Injectable, Injector, Provider } from '@angular/core';
import {
  NavigationError,
  Router,
  ROUTER_CONFIGURATION,
  ɵangular_packages_router_router_h as RouterInitializer,
} from '@angular/router';
import { filter, take } from 'rxjs/operators';

@Injectable()
export class CustomRouterInitializer extends RouterInitializer {
  appInitializer(): Promise<any> {
    const injector: Injector = (this as any).injector;
    const opts = injector.get(ROUTER_CONFIGURATION);

    if (
      opts.initialNavigation ===
      'enabled' /* for Ng11: || opts.initialNavigation === 'enabledBlocking' */
    ) {
      let resolve: any;
      const res = new Promise((r) => (resolve = r));

      injector
        .get(Router)
        .events.pipe(
          // We want to explicitly react on NavigationError event occurs just
          // after the first navigation start event
          take(2),
          filter((event) => event instanceof NavigationError)
        )
        .subscribe(() => resolve(false));

      super.appInitializer().then((val) => resolve(val));
      return res;
    }

    return super.appInitializer();
  }
}

export const routerFixProvider: Provider = {
  provide: RouterInitializer,
  useClass: CustomRouterInitializer,
};

The above workaround is for ng12.
I noticed that in ng13 there's no similar private API available, unless I'm missing something.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions