localize-router: Lazy loading not working for nested lazy routes

Note that at the moment lazy loading doesn’t work for nested routes. If you have route like lazy/lazy_child if you navigate first to lazy and then to lazy_child everything will work. However if you go directly to lazy/lazy_child Router will report unknown route error, since child routes will not be translated before Route matching mechanism kicks in.

Steps to reproduce

  • Create a lazy loaded module with nested routes e.g.
// main routes
{ path: 'lazy', loadChildren: 'path/to/some/route/lazy.module#LazyModule' }

// routes in LazyModule
{ path: '', component: LazyComponent },
{ path: 'child', component: LazyChildComponent }
  • Navigate to translated version of /lazy/child in language other than en e.g.
/de/muessiges/kind

Current behavior Router reports NavigationError as route has not been translated at the moment of route matching.

Expected behavior All child lazy routes should be translated and Router should properly find requested route.

Explanation LocalizeRouter currently listens to RouteConfigLoadEnd, but this event only signals that child module has been loaded. Child routes are still not available. Additionally RouteConfigLoadEnd returns only requested route, not the module factory from which the child routes could be extracted.

Further steps Discuss with Angular team on possible solutions.

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Reactions: 6
  • Comments: 47 (4 by maintainers)

Most upvoted comments

Finally yes. I will implement the solution in the upcoming days.

Looks like this is not working with nested lazy loading… top level lazy loaded components get loaded but lazy loaded components with routes declared with forChild(routes) get the “Cannot match any routes” error

Here’s an example where the terms/privacy policy page work as expected but the lazy loaded module breaks with the Cannot match any routes error:

import { Location } from '@angular/common';
import { HttpClient } from '@angular/common/http';
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { TranslateService } from '@ngx-translate/core';
import { LocalizeRouterModule, LocalizeRouterSettings, LocalizeParser } from 'localize-router';
import { LocalizeRouterHttpLoader } from 'localize-router-http-loader';

export function HttpLoaderFactory(translate: TranslateService, location: Location, settings: LocalizeRouterSettings, http: HttpClient) {
  return new LocalizeRouterHttpLoader(translate, location, settings, http);
}

const routes: Routes = [
  { path: '', loadChildren: './pages/home/home-tabs/home-tabs.module#HomeTabsPageModule' },
  { path: '', loadChildren: './pages/category/category-tabs/category-tabs.module#CategoryTabsPageModule' },
  { path: 'terms', loadChildren: './pages/terms/terms.module#TermsPageModule' },
  { path: 'privacy', loadChildren: './pages/privacy-policy/privacy-policy.module#PrivacyPolicyPageModule' },
  { path: 'search', loadChildren: './pages/search/search.module#SearchPageModule' },
  { path: 'video', loadChildren: './pages/video/video.module#VideoPageModule' }
];

@NgModule({
  imports: [
    LocalizeRouterModule.forRoot(routes, {
      parser: {
        provide: LocalizeParser,
        useFactory: HttpLoaderFactory,
        deps: [TranslateService, Location, LocalizeRouterSettings, HttpClient]
      }
    }),
    RouterModule.forRoot(routes)
  ],
  exports: [LocalizeRouterModule, RouterModule]
})
export class AppRoutingModule { }

And here’s the lazy loaded module’s routes:

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { TranslateModule } from '@ngx-translate/core';
import { LocalizeRouterModule } from 'localize-router';

import { HomeTabsPage } from './home-tabs.page';

const routes: Routes = [
  {
    path: 'browse',
    component: HomeTabsPage,
    children: [
      {
        path: 'popular',
        children: [
          {
            path: '',
            loadChildren: '../popular/home-popular.module#HomePopularPageModule'
          }
        ]
      },
      {
        path: 'latest',
        children: [
          {
            path: '',
            loadChildren: '../latest/home-latest.module#HomeLatestPageModule'
          }
        ]
      },
      {
        path: 'trending',
        children: [
          {
            path: '',
            loadChildren: '../trending/home-trending.module#HomeTrendingPageModule'
          }
        ]
      },
      {
        path: '',
        redirectTo: '/browse/popular',
        pathMatch: 'full'
      }
    ]
  },
  {
    path: '',
    redirectTo: '/browse/popular',
    pathMatch: 'full'
  }
];

@NgModule({
  imports: [
    LocalizeRouterModule.forChild(routes),
    RouterModule.forChild(routes),
    TranslateModule
  ],
  exports: [RouterModule]
})
export class TabsPageRoutingModule {}

Even if this was resolved at any point of time, there had to be a regression. As of today only top level route of a lazy-loaded module (the one with empty string path) is loaded properly, any child routes it contains resolve in Cannot match any routes error. It would be great if we could reopen this issue @meeroslav

can we reopen this issue @meeroslav ? or I can create a new issue with repro…

I have dig a little bit more why doesn’t work in my case and found out that LocalizeRouterService.traverseRouteSnapshot when checking for segments is excluding paths that have ‘empty’ fragments as a parent…

this line:

if (snapshot.firstChild && snapshot.firstChild.routeConfig && snapshot.firstChild.routeConfig.path) {

will skip route configuration when lazy module has default route set as empty with parent component in, something like this:

const lazyRoutes: Routes = [
    {
      path: '',
      component: ContainerComponent,
      children: [
        {
          path: 'child1',
          component: ChildComponent1
        },
        {
          path: 'child2',
          component: ChildComponent2
        }, 
  ]

changeLanguage executed on the path <lazyModulePath>/child1 will not work

A work around to this is to add a route in others translation language. If you have many languages and many subroutes that can be painfull but if like me you have to translate 2 languages, you’re going to be able to use lazyloading and copy paste urls that the users can enter in the browser to arrive directly on the web page.

For Exemple:

In appModule export const routes: Routes = [ { path: ‘’, component: NavigationComponent, children: [ { path: ‘admin’, loadChildren: ‘./+admin/admin.module#AdminModule’ }, { path: ‘users’, loadChildren: ‘./+users/users.module#UsersModule’ } ] } ];

In submodule:

export const routes: Routes = [ { path: ‘’, component: AdminComponent, children: [ { path: ‘’, component: ListUserFormComponent }, { path: ‘profile/:userid’, component: UserDetailComponent }, { path: ‘profile/:userid/cat/:catid’, component: CatDetailComponent }, // english { path: ‘profile/:userid/chat/:catid’, component: CatDetailComponent }, // french { path: ‘profile/:userid/cat/:catid/fur’, component: FurDetailComponent }, // english { path: ‘profile/:userid/chat/:catid/fourrure’, component: FurDetailComponent } // french ] } ];

fr.sjon : “ROUTES.partners”: “partners”, “ROUTES.cat”: “chat”, “ROUTES.fur”: “fourrure”,

en.json: “ROUTES.partners”: “partners”, “ROUTES.cat”: “cat”, “ROUTES.fur”: “fur”,

@timmyrosen This library seems to be dead, we have tried all the suggested fixes in this library but none works. We already migrated to https://github.com/gilsdav/ngx-translate-router and everything works like a charm with a good maintainer support.

Also works completely with Angular Universal.

@meeroslav changeLanguage executed on the path

lazyModulePath/child1

will not work.

some news here?

Currently having the same issue as mentioned here. I’m curious about the solution you came up with.