Skip to content
This repository was archived by the owner on Apr 8, 2020. It is now read-only.

Unable to lazy load modules - Module cannot be found error #1258

Closed
mpalmer-sps opened this issue Sep 5, 2017 · 13 comments
Closed

Unable to lazy load modules - Module cannot be found error #1258

mpalmer-sps opened this issue Sep 5, 2017 · 13 comments

Comments

@mpalmer-sps
Copy link

Hello, I've created a Github repo here: https://github.com/mpalmer-sps/PluralSightNg2Fundamentals

I'm trying to add a module using a lazy load implementation (learning this) but the Angular documented approach does not work despite the syntax being correct. I'm uncertain if this is (maybe) not supported by the SPATemplates?
The error is the following:
ERROR Error: Uncaught (in promise): Error: Cannot find module 'app/user/user.module'. webpackEmptyContext@http://localhost:3258

The user.module is referenced here in the app.module:
{ path: 'user', loadChildren:'app/user/user.module#UsersModule'},

however, this doesn't work even when trying different path strings.

The workaround for this is to modify the navigation link in the navbar.component.html file to the following:
<a [routerLink]="['/profile']" routerLinkActive="active">Welcome User</a>

Whereas before it was the following:
<a [routerLink]="['/user/profile']" routerLinkActive="active">Welcome User</a>

and then removing the path with the loadChildren syntax and adding the UserModule as shown in the app.module.shared.ts file

Is this is expected behavior when using modules other than the app.module?

@SteveSandersonMS
Copy link
Member

Since this is an Angular usage question, could you raise this with the Angular community or see what they have in docs? The templates here supply a typical Webpack+Angular starting point, but I'm afraid we don't have the capacity to also take on usage questions for third-party frameworks and packages.

I'm uncertain if this is (maybe) not supported by the SPATemplates?

The templates here don't affect what is or isn't supported, as they are not doing anything at runtime.

Hope that's OK!

@mpalmer-sps
Copy link
Author

Thanks @SteveSandersonMS ,
What this means is that, if I can't find an answer then I'm basically stuck.

The problem is that using the SPATemplates, with the file structure that is leveraged being so far outside of the 'norm' for Angular development, puts me in a no-man's land for support.

The Microsoft side of the house will point to the Angular side and say "the problem must be over there"..

Inversely, the Angular side of the house will take one look at the project structure and throw their hands in the air because the project structure with app.modules split into three files just to start is extremely difficult to trace where the actual issue sits.

In the end I may just simply have to abandon this approach of using Visual Studio 2017 and the SPATemplates because of that lack of support for what should be a basic Angular implementation in favor of something that is alot more "normal" in the Angular world.

Thanks

@mpalmer-sps
Copy link
Author

@SteveSandersonMS I've tried every possible option to load this Module leveraging the loadChildren method and it doesn't work. Would it be possible that you can provide a super simple example in a "Hello World" way where the loadChildren method is able to find the module?

@MarkPieszak
Copy link
Contributor

You should have it being a relative path, so ./user/user.module#UserModule.

Then make sure your module there has its routes set up correctly. Like in this example: https://github.com/MarkPieszak/aspnetcore-angular2-universal/blob/master/ClientApp/app/containers/lazy/lazy.module.ts

Hope that helps!

@mpalmer-sps
Copy link
Author

Thanks @MarkPieszak
Let me take a look at your solution and see what adjustments need to be made to mine to get this working. One a side note I see that you've stated in comments to look at the network tab to show the module/component being loaded but because everything is bundled into a larger package will this be a test that shows your lazy component being loaded?

@mpalmer-sps
Copy link
Author

@MarkPieszak
ok - update to this. Re-tried various path based approaches that mimic your setup but still no luck so I dug abit deeper.
Based on the Angular documentation here:
https://shlomiassaf.github.io/ng-router-loader/index.html

there is a line here where a Module is lazy loaded by referencing the module after it was already resolved with an import statement as in this example:

import { Routes } from '@angular/router';
import { DetailModule } from '../my-ng-modules/details';

then it was referenced in his code in this way:

export const ROUTES: Routes = [
{ path: 'detail', loadChildren: () => DetailModule },
];

So I took this and applied it to my users module in this way within app.module.shared:

import { UsersModule } from "./user/user.module";

and then added the route as shown below...

{ path: 'user', loadChildren: () => UsersModule },

This seemed to work but I wanted to test and be 100% certain that the module and components were not being loaded until I clicked the appropriate navlink.

Testing this with the network tab on FireFox and Chrome didn't work because of bundling of the JS files into a larger bundle (vendor.js).

So, I placed a pair of constructors in the User Module class and the Profile class that wrote to the console once they loaded and it looks like its working (at least there are no errors).

I'm still scratching my head as to why the syntax you have in your solution works and mine seems to fail, furthermore, I don't fully understand if this approach I've taken is "valid" and produces the benefits of lazy loading that I'm hoping to incorporate.

@davidsekar
Copy link

Comment out following line from webpack.config.js
// resolve: { mainFields: ['main'] }, // <-- comment this

Then lazy loaded chunks will be generated for AoT builds.

@dloukola
Copy link

@davidsekar This saved my day. Thank you. @SteveSandersonMS is this a reason that "mainFields" line is in there? If not, it should likely be removed from the template.

@SteveSandersonMS
Copy link
Member

@dloukola It was necessary at some point in the past to make server-side prerendering work correctly. If it's no longer necessary (perhaps third-party libraries have changed) then you can certainly remove it.

The next update to the Angular template eliminates webpack.config.js completely as it is based on the Angular CLI. So in effect this line will be removed, along with everything else from the file :)

@dloukola
Copy link

@SteveSandersonMS This is great news about the removal of webpack. Do you have any estimate on when we can expect to see that go into the template?

@SteveSandersonMS
Copy link
Member

Please see #1288 for full details. There isn't a specific ETA for release, but hopefully we will have a preview out in about a month.

@jrmcdona
Copy link

@davidsekar I was able to get my 0-chunk.js file to generate after commenting that line of code out. However, I do not see that file loading in the Network tab at all but yet my module still seems to run fine.
I added angular-router-loader since I am trying to preload this module.

Does the chunk get picked up by main-client and main-server?

Here is my webpack

const path = require('path');
const webpack = require('webpack');
const merge = require('webpack-merge');
const AotPlugin = require('@ngtools/webpack').AotPlugin;
const CheckerPlugin = require('awesome-typescript-loader').CheckerPlugin;

module.exports = (env) => {
    // Configuration in common to both client-side and server-side bundles
    const isDevBuild = !(env && env.prod);
    const sharedConfig = {
        stats: { modules: false },
        context: __dirname,
        resolve: { extensions: [ '.js', '.ts' ] },
        output: {
            filename: '[name].js',
            chunkFilename: '[name]-chunk.js',
            publicPath: 'dist/' // Webpack dev middleware, if enabled, handles requests for this URL prefix
        },
        module: {
            rules: [
                { test: /\.ts$/, include: /ClientApp/, use: isDevBuild ? ['awesome-typescript-loader?silent=true', 'angular-router-loader', 'angular2-template-loader'] : '@ngtools/webpack' },
                { test: /\.html$/, use: 'html-loader?minimize=false' },
                { test: /\.css$/, use: [ 'to-string-loader', isDevBuild ? 'css-loader' : 'css-loader?minimize' ] },
                { test: /\.(png|jpg|jpeg|gif|svg)$/, use: 'url-loader?limit=25000' }
            ]
        },
        plugins: [new CheckerPlugin()]
    };

    // Configuration for client-side bundle suitable for running in browsers
    const clientBundleOutputDir = './wwwroot/dist';
    const clientBundleConfig = merge(sharedConfig, {
        entry: { 'main-client': './ClientApp/boot.browser.ts' },
        output: { path: path.join(__dirname, clientBundleOutputDir) },
        plugins: [
            new webpack.DllReferencePlugin({
                context: __dirname,
                manifest: require('./wwwroot/dist/vendor-manifest.json')
            })
        ].concat(isDevBuild ? [
            // Plugins that apply in development builds only
            new webpack.SourceMapDevToolPlugin({
                filename: '[file].map', // Remove this line if you prefer inline source maps
                moduleFilenameTemplate: path.relative(clientBundleOutputDir, '[resourcePath]') // Point sourcemap entries to the original file locations on disk
            })
        ] : [
            // Plugins that apply in production builds only
            new webpack.optimize.UglifyJsPlugin(),
            new AotPlugin({
                tsConfigPath: './tsconfig.json',
                entryModule: path.join(__dirname, 'ClientApp/app/app.module.browser#AppModule'),
                exclude: ['./**/*.server.ts']
            })
        ])
    });

    // Configuration for server-side (prerendering) bundle suitable for running in Node
    const serverBundleConfig = merge(sharedConfig, {
        //resolve: { mainFields: ['main'] },
        entry: { 'main-server': './ClientApp/boot.server.ts' },
        plugins: [
            new webpack.DllReferencePlugin({
                context: __dirname,
                manifest: require('./ClientApp/dist/vendor-manifest.json'),
                sourceType: 'commonjs2',
                name: './vendor'
            })
        ].concat(isDevBuild ? [] : [
            // Plugins that apply in production builds only
            new AotPlugin({
                tsConfigPath: './tsconfig.json',
                entryModule: path.join(__dirname, 'ClientApp/app/app.module.server#AppModule'),
                exclude: ['./**/*.browser.ts']
            })
        ]),
        output: {
            libraryTarget: 'commonjs',
            path: path.join(__dirname, './ClientApp/dist')
        },
        target: 'node',
        devtool: 'inline-source-map'
    });

    return [clientBundleConfig, serverBundleConfig];
};

@brnkgabriel
Copy link

The angular documentation says loadChildren: 'app/customers/customers.module#CustomersModule'.
Changing the path to loadChildren: '../app/customers/customers.module#CustomersModule' worked for me.
Putting "../" before the "app"

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants