Angular2 lazy loading modules: how to create a build package with SystemJS Builder?
Asked Answered
G

2

8

i'm using Angular2 and SystemJS to create a web application. I have some modules in my app and in the router configuration i use lazy loading to open them.

Here is a routing file of my app that lazy loads two modules:

const appRoutes: Routes = [
    { path: '', component: MainComponent,
        canActivate: [AuthGuard],
        children: [
            { path: 'dashboard', component: DashboardComponent },
            { path: 'first-section', loadChildren: 'app/modules/FIRST-SECTION/first-section.module' },
            { path: 'second-section', loadChildren: 'app/modules/SECOND-SECTION/second-section.module' },
            { path: 'documents', component: DocumentsComponent },
            { path: 'users', component: UsersComponent },
            { path: '', redirectTo: 'dashboard', pathMatch: 'full' }
        ]
    }
];

I use Gulp for creating tasks for development server and for production build. For the build i use SystemJS Builder that create the minified JS file for the whole app.

gulp.task('app-bundle', function() {
    var builder = new Builder('src', 'src/systemjs.config.js');

    return builder.buildStatic('app/main.js', 'build/scripts/app.min.js', { minify: true });
});

But... if i try to run a server on the build package the app doesn't work when it tried to run the lazy loaded modules. It gives me the following error:

GET http://127.0.0.1:8080/app/modules/FIRST-SECTION/first-section.module 404 (Not Found)

Here is my systemjs.config.js file:

(function (global) {
    System.config({
        paths: {
            'npm:': './node_modules/',
        },
        map: {
            app: 'app',
            '@angular/core': 'npm:@angular/core/bundles/core.umd.js',
            '@angular/common': 'npm:@angular/common/bundles/common.umd.js',
            '@angular/compiler': 'npm:@angular/compiler/bundles/compiler.umd.js',
            '@angular/platform-browser': 'npm:@angular/platform-browser/bundles/platform-browser.umd.js',
            '@angular/platform-browser-dynamic': 'npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js',
            '@angular/http': 'npm:@angular/http/bundles/http.umd.js',
            '@angular/router': 'npm:@angular/router/bundles/router.umd.js',
            '@angular/forms': 'npm:@angular/forms/bundles/forms.umd.js',
            '@angular/upgrade': 'npm:@angular/upgrade/bundles/upgrade.umd.js',
            'rxjs': 'npm:rxjs',
            'lodash': 'npm:lodash/lodash.min.js',
            'jquery': 'npm:jquery/dist/jquery.min.js',
        },
        packages: {
            app: { main: './main.js', defaultExtension: 'js' },
            rxjs: { defaultExtension: 'js' },
        }
    });
})(this);

EDIT:

As described on SystemJs Builder site with static bundles we don't need SystemJs to load modules. In fact I'm creating a static bundle (with buildStatic, instead of bundle), but since I'm excluding SystemJs it seems there aren't other ways to lazy load modules. So how to create a production build using bundle and then using lazy loading modules but without SystemJS (without systemjs.config.js file in the dist folder)? I see WebPack can do it...

Gentes answered 4/11, 2016 at 12:32 Comment(0)
H
1

See if you can appease system.js by providing a map for the required module. So when system.js finds the path modules/FIRST-SECTION/first-section.module , you tell it : Hey , could you map this path to modules/FIRST-SECTION/first-section.module.js without screaming in the console. Hope this helps .

(function (global) {
    System.config({
        paths: {
            'npm:': './node_modules/',
        },
        map: {
            app: 'app',
            'modules/FIRST-SECTION/first-section.module':'modules/FIRST-SECTION/first-section.module.js',
          .........................
          your other mappings here
          .........................
            'lodash': 'npm:lodash/lodash.min.js',
            'jquery': 'npm:jquery/dist/jquery.min.js',
        },
        packages: {
            app: { main: './main.js', defaultExtension: 'js' },
            rxjs: { defaultExtension: 'js' },
        }
    });
})(this);
Hexachord answered 7/11, 2016 at 1:42 Comment(7)
This doesn't work since buildStatic of SystemJS Builder generates one js file starting from main.js (as you can see in my code). In this case how do i map the lazy loading modules?Gentes
see what you mean. Can you try bundle arithmeric as explained in this post here on SO..#38944757. Let me know if that helpsHexachord
I do not need to discard any dependecies. I just need to use lazy loading on dist bundle.Gentes
I have made an example for you github.com/abidmix/SystemjsBuilderLazyLoad using the heroes tutorial app.. Alive demo is available as well. You can use a similar approach for your application. Will update my answer if this bundling workflow works for youHexachord
Thank you for your reply, but as I said there (github.com/systemjs/builder/issues/729#issuecomment-259076482) your are not creating a bundle ready for the production. You are keeping the development structure.Gentes
I have updated my question (see EDIT). Maybe it could make you to figure out the problem.Gentes
Checkout another repo I cloned and tweaked using an old version of the heroes tutorial. You can further tweak it to give your desired production workflow. github.com/abidmix/SystemBuilderGulpWorkflowHexachord
S
1

in order to have bundles work for your app, you need to define the "bundles" property for systemjs, something like this:

    if (global.ENV === 'production') {
        config.transpiler = 'typescript';
        config.map = {
            'app': 'app',
            '@angular': 'node_modules/@angular',
        };
        config.packages = {
            'app': { main: 'main.js', defaultExtension: 'js' },
            '@angular/core': { main: 'index.js' },
            '@angular/common': { main: 'index.js' },
            '@angular/compiler': { main: 'index.js' },
            '@angular/forms': { main: 'index.js' },
            '@angular/http': { main: 'index.js' },
            '@angular/platform-browser': { main: 'index.js' },
            '@angular/platform-browser-dynamic': { main: 'index.js' },
            '@angular/router': { main: 'index.js' },
        };
        config.bundles = {
            'build/scripts/app.min.js': [
                'app/modules/FIRST-SECTION/*',
                'app/modules/SECOND-SECTION/*',
                '@angular/core/index.js',
                '@angular/common/index.js',
                '@angular/compiler/index.js',
                '@angular/platform-browser/index.js',
                '@angular/platform-browser-dynamic/index.js',
                '@angular/http/index.js',
                '@angular/router/index.js',
                '@angular/forms/index.js',
            ]
        }

System.config(config);
Stockstill answered 8/11, 2016 at 5:23 Comment(7)
Thank you for coming here to reply me :) Anyway your solution doesn't work. I still get 404 on call to lazy loaded module. I added the following code to my systemjs.config.js: bundles: { 'build/scripts/app.min.js': ['app/modules/FIRST-MODULE/*', 'app/modules/SECOND-MODULE/*'] }Gentes
I see, you are creating a static js file so I guess, with static js file you cannot use systemjs to load it dynamically, if you look at the systemjs builder site, they say with static bundles you even don't need system js to load modules, what I do in my solution I create non static bundlesStockstill
Yes, you are right. So how to create a production build without SystemJS (without systemjs.config.js file in the dist folder) and with lazy loading modules? I see WebPack can do it...Gentes
I have updated my question (see EDIT). Maybe it could make you to figure out the problem.Gentes
If you have one static js file for the whole application then it means you don't need to have lazy loading, if you need lazy loading then it means you need several bundles (js files) for your app as per module, so I suggest you use the "build" function of systemjs builder opposed to "buildStatic" so that you can have multiple bundles and then you can have lazy loading, and of course you need system js to dynamically load themStockstill
So we have to include SystemJS loader in production builds?!Gentes
if you have multiple modules in production and you want to load them dynamically (lazy loading in ng2) then you have a sort of dynamic module loader (e.g. systemJs or webPack or ...), to make the app production ready, you need to create bundles (in your case two bundles -first module - second module) and then you need to load them using a dynamic module loader, in my instance I use system jsStockstill

© 2022 - 2024 — McMap. All rights reserved.