6

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.

RyanGled
  • 356
  • 3
  • 9

3 Answers3

5

You can boostrap an array of components from AppModule, something like this:

import { NgModule }      from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import {AppComponent} from "./app.component";
import {MainSidebarComponent} from "./menus/main-sidebar.component";
import {FormsModule} from "@angular/forms";
import {appRouting} from "./app.routes";
import {DashBoardComponent} from "./dashboard/dashboard.component";
import {HomeComponent} from "./home/home.component";
import {LoginComponent} from "./login/login.component";
import {UsersModule} from "./users/users.module";
import {TopBarComponent} from "./menus/top-bar.component";

@NgModule({
    imports:      [
        BrowserModule,
        FormsModule,
        appRouting,

        UsersModule
    ],
    declarations: [
        AppComponent,
        MainSidebarComponent,
        TopBarComponent,
        DashBoardComponent,
        HomeComponent,
        LoginComponent
    ],
    bootstrap:    [ AppComponent, MainSidebarComponent, TopBarComponent ]
})
export class AppModule { }

Then in your index.html

<!doctype html>
<head>
    <base href="/">
    <meta charset="UTF-8">

    <!-- 1. Load libraries -->
    <!-- Polyfill(s) for older browsers -->
    <script src="node_modules/core-js/client/shim.min.js"></script>
    <script src="node_modules/zone.js/dist/zone.js"></script>
    <script src="node_modules/reflect-metadata/Reflect.js"></script>
    <script src="node_modules/systemjs/dist/system.src.js"></script>
    <!-- 2. Configure SystemJS -->
    <script src="systemjs.config.js"></script>
    <script>
        System.import('app').catch(function (err) {
            console.error(err);
        });
    </script>
</head>
<body class=" sidebar_main_open sidebar_main_swipe">

<header id="header_main">
 <top-bar></top-bar>
</header><


<aside id="sidebar_main">
    <main-sidebar></main-sidebar>
</aside>


<div id="page_content">
    <div id="page_content_inner">
        <my-app>Loading....</my-app>
    </div>
</div>
</body>
</html>
jcmordan
  • 948
  • 12
  • 19
4

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.

RyanGled
  • 356
  • 3
  • 9
  • https://www.tutorialspoint.com/angular2/angular2_modules.htm is also very helpful to understand SystemJS and Angular 2 Modules. – RyanGled Oct 04 '16 at 11:25
  • How did you manage to do this in production when running ng build --prod? it creates issues in the last code example: https://stackoverflow.com/questions/52892251/bootstrap-multiple-or-single-modules-based-on-url-in-main-ts – CularBytes Oct 19 '18 at 14:48
  • @CularBytes this was over 2 years ago, long before Angular CLI dealt with all the AoT complexity for you. It's definitely out of date now. – RyanGled Oct 21 '18 at 09:53
0

I don't know if you can achieve the exact solution you want, but you could try to lazy-load some of your modules.

For instance, you create an AppModule (used in bootstrapping). In AppModule you only import your "home" module (I'm gonna call it HomeModule).

Than all the other modules can be lazy-loaded (reference)

Tiba
  • 244
  • 2
  • 5
  • I'm not sure if I've understood correctly, but I'm not using routing, just static selectors working in tandem in the same view. Both and work perfectly when systemJS calls them separately, but unfortunately they won't load together; possibly a systemJS config issue or an Angular2 DI issue? – RyanGled Oct 04 '16 at 08:22