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

Cannot read property 'loadChildren' of undefined #4686

Closed
fulls1z3 opened this issue Feb 14, 2017 · 21 comments
Closed

Cannot read property 'loadChildren' of undefined #4686

fulls1z3 opened this issue Feb 14, 2017 · 21 comments

Comments

@fulls1z3
Copy link

@fulls1z3 fulls1z3 commented Feb 14, 2017

Please provide us with the following information:

OS?

Windows 10

Versions.

@angular/cli: 1.0.0-beta.30
node: 6.9.1
os: win32 x64
@angular/common: 2.4.7
@angular/compiler: 2.4.7
@angular/core: 2.4.7
@angular/forms: 2.4.7
@angular/http: 2.4.7
@angular/platform-browser: 2.4.7
@angular/platform-browser-dynamic: 2.4.7
@angular/router: 3.4.7
@angular/cli: 1.0.0-beta.30
@angular/compiler-cli: 2.4.7

Repro steps.

feature.module.ts
Using @nglibs/i18n-router to provide route translations for feature modules (lazy loaded).

import { I18NRouterModule } from '@nglibs/i18n-router';

@NgModule({
  imports: [
    CommonModule,
    // RouterModule.forChild(routes)
    I18NRouterModule.forChild(routes, 'about')
  ],
  declarations: [
    AboutComponent,
    AboutUsComponent,
    AboutBananaComponent,
    AboutApplePearComponent
  ]
})

The whole solution is located on github: angular-cli branch of @nglibs/example-app.

The log given by the failure.

Can't talk about a stack trace, but hope it will be helpful:

$ ng serve
** NG Live Development Server is running on http://localhost:4200. **
Hash: ddefd75abb9ca8b82bd4                                                      Time: 18958ms
chunk    {0} polyfills.bundle.js, polyfills.bundle.map (polyfills) 222 kB {4} [initial] [rendered]
chunk    {1} main.bundle.js, main.bundle.map (main) 8.61 kB {3} [initial] [rendered]
chunk    {2} styles.bundle.js, styles.bundle.map (styles) 10 kB {4} [initial] [rendered]
chunk    {3} vendor.bundle.js, vendor.bundle.map (vendor) 3.49 MB [initial] [rendered]
chunk    {4} inline.bundle.js, inline.bundle.map (inline) 0 bytes [entry] [rendered]

ERROR in Cannot read property 'loadChildren' of undefined
webpack: Failed to compile.

Mention any other details that might be useful.

@nglibs/i18n-router does not work with @angular-cli (yet), and giving the following error during [AoT compilation]:

ERROR in Cannot read property 'loadChildren' of undefined

I suppose @angular-cli uses @ngtools/webpack for [AoT compilation], expecting RouterModule.forChild(...) to resolve lazy-loaded modules (with loadChildren), which is replaced by I18NRouterModule.forChild(...) - providing routes for feature modules instead.

Here's the piece of code from i18n-router module line 72, which provides child routes.

                {
                    provide: ROUTES,
                    useFactory: (provideChildRoutes),
                    deps: [I18NRouterService, RAW_ROUTES, MODULE_KEY],
                    multi: true
                },
                {
                    provide: ANALYZE_FOR_ENTRY_COMPONENTS,
                    useValue: routes,
                    multi: true
                }

It provides ROUTES and ANALYZE_FOR_ENTRY_COMPONENTS to Angular the same way that the router module does (see below):

However, angular-cli seems insisting ignoring the provided child routes and firing errors.

To resolve this issue temporarily, I switched using [ng-router-loader]. Hence @angular-cli doesn't allow modifying the webpack configuration, I need to manually configure build tools (dev/prod sever, task runners, webpack, etc).

I think loadChildren should be able to recognize routes provided with ROUTES and ANALYZE_FOR_ENTRY_COMPONENTS.

@Meligy

This comment has been minimized.

Copy link
Contributor

@Meligy Meligy commented Feb 14, 2017

That's where the lazy route discovery is happening:

https://github.com/angular/angular/blob/master/modules/%40angular/compiler-cli/src/ngtools_impl.ts#L56

I wasn't even aware there're other alternatives to this, but to start with, it seems to be considering the ROUTES bit:

// We cannot depend directly to @angular/router.
type Route = any;
const ROUTER_MODULE_PATH = '@angular/router/src/router_config_loader';
const ROUTER_ROUTES_SYMBOL_NAME = 'ROUTES';

...

  const ROUTES = reflector.findDeclaration(ROUTER_MODULE_PATH, ROUTER_ROUTES_SYMBOL_NAME);

No idea about ANALYZE_FOR_ENTRY_COMPONENTS (maybe the core team of the CLI has better idea).

I wouldn't expect focus on this one from the core team before v1.0 final (could be wrong). Would you have any idea extending the above file to cater for your case?

@filipesilva

This comment has been minimized.

Copy link
Member

@filipesilva filipesilva commented Mar 13, 2017

I think this is essentially the same issue as #4541, please have a look.

@fulls1z3

This comment has been minimized.

Copy link
Author

@fulls1z3 fulls1z3 commented Mar 14, 2017

Hi @filipesilva, I've read #4541 and @christopherthielen talks about more or less a similar functionality.

Currently waiting for updates, and many thanks for your attention 👍

@fulls1z3

This comment has been minimized.

Copy link
Author

@fulls1z3 fulls1z3 commented Mar 20, 2017

First of all I'd like to say thanks to @christopherthielen, for all the details he provided on #4541. This information was very helpful while analyzing the issue with @nglibs/i18n-router.

As far as I can progress, I've found out that if I provide child routes unprocessed, then the @ngtools/webpack can find the lazy routes.

        {
          provide: ROUTES,
          useValue: routes,
          // useFactory: (provideChildRoutes),
          // deps: [I18NRouterService, RAW_ROUTES, MODULE_KEY],
          multi: true
        },
        {
          provide: ANALYZE_FOR_ENTRY_COMPONENTS,
          useValue: routes,
          multi: true
        }

Now, the challenge is to get the @ngtools/webpack identify routes provided by useFactory.

@bevlison

This comment has been minimized.

Copy link

@bevlison bevlison commented Feb 20, 2018

I had the error. Found the cause in my case, want to share
in the TS app-routes module (of angular 5 project) I made a syntax error :

const routes: Routes = [
{ path: '', redirectTo:'/', pathMatch: 'full' }
, { path: 'a', component : aComponent }
, { path: 'a/detail/:id', component: aDetailComponent },
, { path: 'b', component : bComponent }
, { path: 'b/detail/:id', component : bDetailComponent }
];

The error is the

,

at the end of the 4th line, which delivers an empty element in the Routes array, which delivers the loadChildren error.

@akash-potdar7

This comment has been minimized.

Copy link

@akash-potdar7 akash-potdar7 commented Jun 25, 2018

I had the same error as well, but the problem was I had move the app-router.module.ts outside app folder.

@tomsaleeba

This comment has been minimized.

Copy link

@tomsaleeba tomsaleeba commented Jun 27, 2018

I had the same error but yet another cause. I was dynamically assigning a key to the routes object:

const routes: Routes = [{
  path: '',
  component: SomeComponent,
  children: [{
    path: 'blah',
    component: BlahCreateComponent,
    resolve: {
      [SOME_CONSTANT]: SomeResolver, // <-- you can't do this
    },
  }],
}]

I was only able to track down the issue by doing ng build --prod --aot which gave me an more descriptive error.

@thiagofrancisquete

This comment has been minimized.

Copy link

@thiagofrancisquete thiagofrancisquete commented Sep 2, 2018

@tomsaleeba and how you fixed it? you just deleted the resolve part?

@korenb

This comment has been minimized.

Copy link

@korenb korenb commented Sep 3, 2018

@tomsaleeba
thank you for this clue.
i did similar thing: i set a constant as key

{
    path: 'feature',
    loadChildren: '...path-to-module...',
    data: {
        'menu-bar': MenuBar.Show,
        [AFTER_LOGIN_PRELOADING_STRATEGY]: true
    }
}

it's properly to set a key as direct definition or instant string value like data: { YEAR: 2018, 'month-text': 'August' }

@tomsaleeba

This comment has been minimized.

Copy link

@tomsaleeba tomsaleeba commented Sep 6, 2018

@thiagofrancisquete as @korenb mentioned, it seems like you can't dynamically set the key of the object (using that syntax with the square braces [SOME_KEY]. Instead, you need to directly define the key.

As a concrete example, assume I was trying to assing the dynamic key like this

const SOME_CONSTANT = 'theKey'
...
    resolve: {
      [SOME_CONSTANT]: SomeResolver,
    },
...

...to fix it, you need to do this:

...
    resolve: {
      theKey: SomeResolver,
    },
...
@vsagar9944

This comment has been minimized.

Copy link

@vsagar9944 vsagar9944 commented Sep 13, 2018

I had faced same issue. In My case I had not included my Route resolver in provider property of NgModule.
After declaring in provider this issue get fixed.

@jersonjcnt

This comment has been minimized.

Copy link

@jersonjcnt jersonjcnt commented Oct 17, 2018

as I commented @bevlison I also had syntax error [ , , ]
export const AuthRoutes: Routes = [ { path: '', redirectTo: 'signin', pathMatch: 'full' }, { path: 'signin', component: SigninComponent }, { path: 'signup', component: SignupComponent }, { path: 'reset-password', component: ResetPasswordComponent }, { path: 'recover-email', component: RecoverEmailComponent }, , { path: 'verify-email', component: VerifyEmailComponent }];

@klst-speterson

This comment has been minimized.

Copy link

@klst-speterson klst-speterson commented Jan 3, 2019

One final gotcha to contribute:

import { urls } from 'consts'

const { topLevelRoutes } = urls

const routes = [
    { path: topLevelRoutes.login, loadChildren: 'path/login.module#LoginModule' },
    { path: topLevelRoutes.dashboard, loadChildren: 'path/dashboard.module#DashboardModule' },
    { path: '**', redirectTo: topLevelRoutes.dashboard }
]

Fails with the aforementioned error.

import { urls } from 'consts'

// const { topLevelRoutes } = urls

const routes = [
    { path: urls.topLevelRoutes.login, loadChildren: 'path/login.module#LoginModule' },
    { path: urls.topLevelRoutes.dashboard, loadChildren: 'path/dashboard.module#DashboardModule' },
    { path: '**', redirectTo: urls.topLevelRoutes.dashboard }
]

Works fine.

Not sure if it's only a dev server thing and works with a proper build or not. Also not sure about any permutations. Obviously not a pain point, just wanted to add this in case somehow runs into a similar circumstance.

@RasicN

This comment has been minimized.

Copy link

@RasicN RasicN commented Jan 28, 2019

I came to this issue with a similar problem and actually found my fix here.

The problem I was having was I did not have the export on my function as soon as I added export to it I could build again. Sorry if this isn't specifically related to the OP but thought it may help someone.

@sanidz

This comment has been minimized.

Copy link

@sanidz sanidz commented Feb 19, 2019

I had the same error but yet another cause. I was dynamically assigning a key to the routes object:

const routes: Routes = [{
  path: '',
  component: SomeComponent,
  children: [{
    path: 'blah',
    component: BlahCreateComponent,
    resolve: {
      [SOME_CONSTANT]: SomeResolver, // <-- you can't do this
    },
  }],
}]

I was only able to track down the issue by doing ng build --prod --aot which gave me an more descriptive error.

Sure, in my case this did the trick:

const routes: Routes = [];
routes.push(...links.map(link => <Route> { path: link.url, component: link.component }));

@esiva

This comment has been minimized.

Copy link

@esiva esiva commented Apr 16, 2019

run angular using aot option to get a full descriptive error
ng s --aot

@Uirado

This comment has been minimized.

Copy link

@Uirado Uirado commented Jun 28, 2019

I faced this error as well but for another reason.
I was passing a regex as value inside the data:
data: { regex: /^([0-9]+)$/ }

I had to convert it to a string:
data: { regex: '^([0-9]+)$' }

@rcollette

This comment has been minimized.

Copy link

@rcollette rcollette commented Jul 8, 2019

I know this thread is becoming like stack overflow, but it's the first hit on Google. Here was my scenario.

Cause: routes was not defined as a constant.

I had a route configuration like:

let routes: Routes;
routes = [
  {
    path: '',
    children: [
      {
        path: '',
        children: [
          {
            path: '',
            children: [
              {path: '', component: HeaderComponent, outlet: 'header'},
              {path: '', component: FooterComponent, outlet: 'footer'}
              // Routes that display a header and footer go here.
            ]
          },
          {
            path: '',
            children: [
              {
                path: '/frame',
                children: [{
                  path: 'search',
                  loadChildren: () => import('./search/search.module').then(module => module.SearchModule)
                }]
              }
            ]
          }
        ]
      }
    ]
  }
];

Very specifically, while I was in ng serve (npm start) mode and the browser was at the /frame/search path, I modified the routes to include a ** path (just randomly adding stuff from another working application).

let routes: Routes;
routes = [
  {
    path: '',
    children: [
      {
        path: '',
        children: [
          {
            path: '',
            children: [
              {path: '', component: HeaderComponent, outlet: 'header'},
              {path: '', component: FooterComponent, outlet: 'footer'}
              // Routes that display a header and footer go here.
            ]
          },
          {
            path: '',
            children: [
              {
                path: '/frame',
                children: [{
                  path: 'search',
                  loadChildren: () => import('./search/search.module').then(module => module.SearchModule)
                }]
              }
            ]
          }
        ]
      }
    ]
  },
  {
    path: '**',
    redirectTo: '/frame/search',
    pathMatch: 'full'
  }
];

The browser refreshed and there was a console error:

Uncaught Error: Component HeaderComponent is not part of any NgModule or the module has not been imported into your module.

So sure enough, I had not added those imports to the app.module. I don't know if the error message presented simply by making any change or by adding this specific route change. I do know that if I ran npm start after adding the ** path, I could not get the console error to present.

After adding those imports I got another console error:

Invalid configuration of route '/frame': path cannot start with a slash

I fixed that:

let routes: Routes
routes = [
  {
    path: '',
    children: [
      {
        path: '',
        children: [
          {
            path: '',
            children: [
              {path: '', component: HeaderComponent, outlet: 'header'},
              {path: '', component: FooterComponent, outlet: 'footer'}
              // Routes that display a header and footer go here.
            ]
          },
          {
            path: '',
            data: {header: false, footer: false},
            children: [
              {
                path: 'frame',
                children: [{
                  path: 'search',
                  loadChildren: () => import('./search/search.module').then(module => module.SearchModule)
                }]
              }
            ]
          }
        ]
      }
    ]
  },
  {
    path: '**',
    redirectTo: '/frame/search',
    pathMatch: 'full'
  }
];

And I was good to go (or so I thought).

npm start was still throwing the error.

Comparing the route files between a new project and mine, I noticed that routes was defined as a constant. I probably had copied in the routes from a prior project, or WebStorm modified the code somehow, not sure.

So finally my working app-routes.

const routes: Routes = [
  {
    path: '',
    children: [
      {
        path: '',
        children: [
          {
            path: '',
            children: [
              {path: '', component: HeaderComponent, outlet: 'header'},
              {path: '', component: FooterComponent, outlet: 'footer'}
              // Routes that display a header and footer go here.
            ]
          },
          {
            path: '',
            data: {header: false, footer: false},
            children: [
              {
                path: 'frame',
                children: [{
                  path: 'search',
                  loadChildren: () => import('./search/search.module').then(module => module.SearchModule)
                }]
              }
            ]
          }
        ]
      }
    ]
  },
  {
    path: '**',
    redirectTo: '/frame/search',
    pathMatch: 'full'
  }
];
@vladimiry

This comment has been minimized.

Copy link

@vladimiry vladimiry commented Jul 18, 2019

By the way, using constants for defining the route props works if you compile with Ivy renderer.

@HighSoftWare96

This comment has been minimized.

Copy link

@HighSoftWare96 HighSoftWare96 commented Jul 26, 2019

I had the same error but yet another cause. I was dynamically assigning a key to the routes object:

const routes: Routes = [{
  path: '',
  component: SomeComponent,
  children: [{
    path: 'blah',
    component: BlahCreateComponent,
    resolve: {
      [SOME_CONSTANT]: SomeResolver, // <-- you can't do this
    },
  }],
}]

I was only able to track down the issue by doing ng build --prod --aot which gave me an more descriptive error.

This should be working however, it is not so unusual to use constant keys for routing data that eventually you have to retrieve in other places of the application...

My workaround was something like this:

const routesData = {
  data: {
    [CONSTANT_1]: 'PARAM_VALUE_1',
    [CONSTANT_2]: 'PARAM_VALUE_2'
  }
}

const routes: Routes = [
  {
    path: 'myPath',
    component: MyComponent,
    ...routesData,
    // ... other routes configuration params
  }
];

This actually works because the constant key values are resolved before the routes definition so there are no compilation errors.

@angular-automatic-lock-bot

This comment has been minimized.

Copy link

@angular-automatic-lock-bot angular-automatic-lock-bot bot commented Sep 9, 2019

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.

@angular-automatic-lock-bot angular-automatic-lock-bot bot locked and limited conversation to collaborators Sep 9, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
You can’t perform that action at this time.