Angular-cli: Dynamic Routing with AOT

Created on 30 Mar 2017  Â·  23Comments  Â·  Source: angular/angular-cli

I'm working on a app where i need to have a logic in routing to decide which page should be the home page, it works fine with JIT but with AOT. Here is how my code looks like

       let subdomain = environment.production ? window.location.host.replace('www.', '').split('.')[0] : 'sahiwal';
       let districts = {sahiwal:'121', faisalabad:'122'}
        export const routes: Routes = [
            {
                path: '',
                component: MasterComponent,
                children: [
                    {
                        path: districts[subdomain] ? '' : 'home',
                        loadChildren: './home.module#HomeModule',
                        data: {title: 'سجاگ'}
                    }, {
                        path: subdomain === domain ? '' : 'main',
                        loadChildren: './main.page.module#MainPageModule',
                        data: {title: 'سجاگ'}
                    },
                    {
                        path: 'news',
                        loadChildren: './news.module#NewsModule',
                        data: {title: 'خبر سجاگ'}
                    }, {
                        path: subdomain === 'features' ? '' : 'features',
                        loadChildren: './archive.module#ArchiveModule',
                        data: {title: 'خبر سجاگ'}
                    }
            }
        ];

Versions

@angular/cli: 1.0.0
node: 6.9.1
os: darwin x64
@angular/common: 4.0.1
@angular/compiler: 4.0.1
@angular/animations: 4.0.1
@angular/core: 4.0.1
@angular/forms: 4.0.1
@angular/http: 4.0.1
@angular/platform-browser: 4.0.1
@angular/platform-browser-dynamic: 4.0.1
@angular/platform-server: 4.0.1
@angular/router: 4.0.1
@angular/cli: 1.0.0
@angular/compiler-cli: 4.0.1

Most helpful comment

Solution for Angular 6+

configure your routes as usual:

export const routes: Routes = [
  { path: 'main', component: MyComponent }
]

then change routes array as desired and reset routes at app runtime (in app.component.ts contsructor)

// app.component.ts
constructor(private router: Router) {
  // modify routes
  if (subdomain === domain) {
   routes[0].path = ''; // or something more complex
  }
  // reset routes
  router.resetConfig(routes);
}

It also works with lazyloaded routes.

All 23 comments

For AOT your routes need to be statically analyzable. I'd your need dynamic values in your routes can you use route params instead?

Thanks @deebloo, if you look at my example code i'm getting subdomain from the url and then in routes loading relevant lazy module. somehow i need to do this in routes.
its a funny question but let me ask you, is there not any way to compile only routes with JIT and all other in AOT? I don't think its possible but let me know what you think?

You can't do that in the routes. It looks like the only concern here is the actual name of the routes? and no it is not possible

Okay thank you so much @deebloo, i'm gonna close this issue.

I am having the same problem.

I save routes in my format that I need because of route localization.
Then I have a function that translates my format to the array of routes router understands.

Looks it can't work with AOT like that.

Any workaround?

@iridiumsoft

Does lazy loading working well for you ?

how is your configuration i am having problems with lazy loading

I cant get my routes working in AOT either. Works fine in JIT with ng serve

These are my routes that only don't work in AOT:

import { Route} from '@angular/router';
import { HomeComponent } from './home.component';

export const HomeRoutes: Route[] = [
{
path: '',
component: HomeComponent
},
{
path: 'home',
component: HomeComponent
}
];

So if use dynamic routes must close AOT...

This can be solved by providing routes definition to ROUTES using function factory... (tested on Angular 4.1.2)

Modify your AppModule (app.module.ts):

change RouterModul.forRoot in imports array

imports: [
RouterModule.forRoot(routes)
...

to

RouterModule.forRoot([])

add this two providers in providers array:

... ANALYZE_FOR_ENTRY_COMPONENTS
} from '@angular/core';
...  ROUTES,
} from '@angular/router';
...
providers: [
{provide: ANALYZE_FOR_ENTRY_COMPONENTS, multi: true, useValue: routes},
{provide: ROUTES, multi: true, useFactory: getRoutes},
...

define factory function (at top of the app.module.ts file):

export function getRoutes() {
  return routes;
}

maybe you need also create new file eg: app.routes.ts and put your routes definitions there

export const routes: Routes = [
            {
                path: '',
...

and import it in AppModule (app.module.ts).

import { routes } from './app.routes';

Similar problem here... I have dynamic yet synchronous routes that have to be possible to turn off in config file. I couldn't filter them right in definition due to "function calls are not supported" error and I finally did it in my module constructor by operating on router.config... The routes are added properly and work in JiT and I think they was also working in AoT last time, but now they do not. Any workaround or better solution to make it work in AoT compilation?

Edit: My problem was caused by code minification, not strictly by AoT compiler. I compared component names to find specific route and it found other one with the same name in AoT. After fixing that, my dynamically filtered routes work as expected.

@DanielKucal What do you mean by you found another route with the same name in AoT? Can you please explain this to me? I have the same problem, dynamic routes from file are throwing an exception .. Thank you for you help!

@raujonas, if I remember that correctly I was comparing class names (which are being replaced during minification). The solution was to add a static field to classes and compare it while matching the routes instead.

Bad news in 2018, {provide: ROUTES, multi: true, useFactory: getRoutes} does not work today.

fix(compiler-cli): ngtools should accept useFactory (lazy routes)

Solution for Angular 6+

configure your routes as usual:

export const routes: Routes = [
  { path: 'main', component: MyComponent }
]

then change routes array as desired and reset routes at app runtime (in app.component.ts contsructor)

// app.component.ts
constructor(private router: Router) {
  // modify routes
  if (subdomain === domain) {
   routes[0].path = ''; // or something more complex
  }
  // reset routes
  router.resetConfig(routes);
}

It also works with lazyloaded routes.

@lpikora Does it work in AOT mode? I am guessing it is not.

@liujingbreak yes, it works with AOT. The key is using router.resetConfig(routes) with customised routes array in runtime.

Thank you @lpikora, very nice of your solution.
Also I found some obvious limitations in AOT mode, the fact is we can only change route configure with router.resetConfig(), but we can't bring new route configure like a new lazy module at runtime, it will report error

core.js:14597 ERROR Error: Uncaught (in promise): Error: Cannot find module './lazy/lazy.module.ngfactory'
Error: Cannot find module './lazy/lazy.module.ngfactory'

Angular AOT engine goes through original route configure tree to find components and modules, compile them into ngfactory at compilation time. Any stuff new at runtime will be reported as missing ngfactory by Angular.

And also I have not try it in Angular universal server-side rendering mode.

@liujingbreak have you found any possible approach to make it works with AOT?

@unspike I guess No, there is no ideal approach, since Angular AOT means all the route configurations must be laid down at compile time, not at run time.

  • As long as there is no new route added during runtime, we can adopt @lpikora 's approach.

  • If there is business requirement asks for such dynamic route to be created on-the-fly, then that route can not be a part of the original AOT appliaction.

  • My project does not ask me to have dynamic route on-the-fly, but I does hope we can resuse business route modules as much as possible cross different products like libraries, so we kinda use a tool to read our customized project configuration and have build script to rewrite TS source file before AOT build runs. As a workaround, quite like a Angular command line plugin.

Any one have any updates? I need this too?

I got it working using the ROUTES injection token.
So it seems to still be possible with NG 8.1.

export function getConfigRoutes():Route {
    return CONFIG_SUB_MODULE.getRoute();
}
@NgModule({
    imports: [RouterModule.forChild([])],
    providers: [
        {
            provide: ROUTES,
            multi: true,
            useFactory: getConfigRoutes,
            useValue: {}
        },
    ],
    exports: [RouterModule]
})
export class ConfigRoutingModule {
}

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

_This action has been performed automatically by a bot._

Was this page helpful?
0 / 5 - 0 ratings

Related issues

purushottamjha picture purushottamjha  Â·  3Comments

brtnshrdr picture brtnshrdr  Â·  3Comments

IngvarKofoed picture IngvarKofoed  Â·  3Comments

rajjejosefsson picture rajjejosefsson  Â·  3Comments

hareeshav picture hareeshav  Â·  3Comments