Search code examples
angularsystemjsangular2-modules

Multiple modules in Angular 2


I have an Angular 2 app (RC7) which started as a single component but is quickly becoming used throughout the project in various different (and sometimes completely unrelated) ways.

As a result of this, a single NgModule bootstrapping all the components seems like a terrible idea with a huge amount of bloat. I am struggling to find a way to have multiple modules (Module A would contain Component A, Module B would have Component B, etc) and still allow the bootstrapping of multiple A2 applications on a single page.

In practice, what I am trying to achieve is this:

Page 1 bootstraps Module A - this works.

Page 2 bootstraps Module B - this works.

Page 3 bootstraps Module A & Module B - this doesn't work.

index.html

<script src="/angular2/node_modules/zone.js/dist/zone.js"></script>
<script src="/angular2/node_modules/reflect-metadata/Reflect.js"></script>
<script src="/angular2/node_modules/systemjs/dist/system.src.js"></script>
<script src="/angular2/systemjs.config.js"></script>


<script type="text/javascript">
    System.import('appOne').catch(function(err){ console.error(err); });
    System.import('appTwo').catch(function(err){ console.error(err); });
</script>

systemjs.config.js

(function (global) {
    System.config({
        paths: {
            'npm:': '/angular2/node_modules/'
        },
        map: {
            appOne: '/angular2/',
            appTwo: '/angular2/',
        },
        packages: {
            appOne: {
                main: './appOne.main.js',
                defaultExtension: 'js'
            },
            appTwo: {
                main: './appTwo.main.js',
                defaultExtension: 'js'
            },
        }
    });
})(this);

AppOne and AppTwo's modules simply bootstrap the relevant component (componentOne & componentTwo). However, both will not render on the same page at the same time. I have no errors in the console, but whichever package is listed last within the systemjs.config.js file is the only one to load.

Can anyone see why it might not be working? Or perhaps recommend another way to structure my modules. I'd rather not have one parent module with all components, services, etc listed - this seems to defeat the point of Angular 2's nested Component trees, and will eventually affect page-load times exponentially.

Thanks in advance.


Solution

  • For anyone interested, the issue was bootstrapping. Both apps have to be bootstrapped in unison in order to work.

    index.html

    <script src="/angular2/node_modules/zone.js/dist/zone.js"></script>
    <script src="/angular2/node_modules/reflect-metadata/Reflect.js"></script>
    <script src="/angular2/node_modules/systemjs/dist/system.src.js"></script>
    <script src="/angular2/systemjs.config.js"></script>
    
    
    <script type="text/javascript">
        System.import('appOneAndTwo').catch(function(err){ console.error(err); });
    </script>
    

    systemjs.config.js

    (function (global) {
        System.config({
            paths: {
                'npm:': '/angular2/node_modules/'
            },
            map: {
                appOne: '/angular2/',
                appTwo: '/angular2/',
                appOneAndTwo: '/angular2/',
            },
            packages: {
                appOne: {
                    main: './appOne.main.js',
                    defaultExtension: 'js'
                },
                appTwo: {
                    main: './appTwo.main.js',
                    defaultExtension: 'js'
                },
                appOneAndTwo: {
                    main: './appOneAndTwo.main.js',
                    defaultExtension: 'js'
                },
            }
        });
    })(this);
    

    appOneAndTwo.main.ts

    import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
    import {AppOneModule} from './app/app.one.module';
    import {AppTwoModule} from './app/app.two.module';
    
    platformBrowserDynamic().bootstrapModule(CommentAppModule);
    platformBrowserDynamic().bootstrapModule(WorkflowAppModule);
    

    It's still not ideal as I have to create a new main.ts file for every combination of Angular 2 apps I create, but it does mean the imports are only imported strictly when necessary and the payload isn't bloated to the end user.

    I am currently looked at the AoT compiler (https://angular.io/docs/ts/latest/cookbook/aot-compiler.html) and uglification/minification using Webpack to further reduce payload size.