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?
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!
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
@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?
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!
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?
@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.
Comment out following line from webpack.config.js
// resolve: { mainFields: ['main'] }, // <-- comment this
Then lazy loaded chunks will be generated for AoT builds.
@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.
@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 :)
@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?
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.
@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];
};
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"
Most helpful comment
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