angularangular-routingangular-providersangular-standalone-componentsangular-ssr

Angular 17 - SSR Standalone - Routing FactoryProvider with async - NG04014


Introduction

Hello, I'm trying to configure my angular router and pass it different routes depending on whether or not we are authenticated.

Here is my code to do this:

app.routes.ts

export const authenticatedRoutes: Routes = [
  {
    path: 'test',
    component: DashboardPage,
  },
  {
    path: '**',
    redirectTo: 'test'
  }
];

export const unauthenticatedRoutes: Routes = [
  {
    path: 'test',
    component: HomePage,
  },
  {
    path: '**',
    redirectTo: 'test'
  }
];

export const provideRoutes = () => {
  const provider: Provider[] = [{
    provide: ROUTES,
    useFactory: async (oidcSecurityService: OidcSecurityService) => {
      const { isAuthenticated } = await firstValueFrom(oidcSecurityService.checkAuth())
      if (isAuthenticated) return authenticatedRoutes
      return unauthenticatedRoutes
    },
    multi: true
  }]
  return makeEnvironmentProviders(provider)
}

app.config.ts

import { provideRoutes } from '@/configurations'

export const appConfig: ApplicationConfig = {
  providers: [
    provideRoutes(),
    // others providers...
  ]
};

Research

When I run "ng build --watch --configuration development", I got this error

An unhandled exception occurred: NG04014: Invalid configuration of route ''. One of the following must be provided: component, loadComponent, redirectTo, children or loadChildren

The problem comes from the async factory, this code works:

export const provideRoutes = () => {
  const provider: Provider[] = [{
    provide: ROUTES,
    useFactory: () => {
      return unauthenticatedRoutes
    },
    multi: true
  }]
  return makeEnvironmentProviders(provider)
}

But I have no idea how to solve this problem. Do you know what I should do?


Solution

  • Asynchronous providers are not supported by Angular (see issue).

    You're probably looking for route guards, and more specifically : CanActivate

    Your guard

    export const yourGuardFunction: CanActivateFn = (
        next: ActivatedRouteSnapshot,
        state: RouterStateSnapshot) => {
          // your  logic goes here
      }
    

    Your route:

    {
      path: '/your-path',
      component: YourComponent,
      canActivate: [yourGuardFunction],
    }