Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / HTML

A Note on Angular 2 Bootstrap Multiple Apps

4.10/5 (5 votes)
7 Mar 2017CPOL5 min read 29.3K   355  
This is a note on how to bootstrap multiple apps in a single HTML page.

Introduction

This is a note on how to bootstrap multiple apps in a single HTML page.

Background

The Angular's website has examples on how to bootstrap an Angular application, but the examples only bootstrap a single application in an HTML page. It is not absolutely necessary, but you may find the need to bootstrap multiple applications in a single HTML page.

  • Structural reason - In an ASP.NET MVC application, you typically put the components common to all the pages into the layout page. You may want to bootstrap these components separately from the page specific components;
  • Performance reason - By default, the scope of the Angular's change detection is the whole component tree in the application. If you know that the actions (DOM events, and callbacks) in certain components have no impact on the states of the other components, you may want to bootstrap them separately, so the change detection runs in a smaller scope.

The attached example is an ASP.NET MVC application in Visual Studio 2015 Update 3. If you want to run it, I strongly recommend you take a look at my early posting, because it is not a trivial task to compile and run an Angular 2 application of Typescript.

Image 1

The example bootstraps two Angular applications in a single HTML page.

  • The components for each Angular application are in the "app1" and "app2" folders
  • The "shared-sub-component" and the "math-service" are shared in both Angular applications to demonstrate that different bootstraps can share common code
  • Both Angular applications will be bootstrapped in the "Index.cshtml" page

The following sections are a little verbose just for completeness. If you are interested in bootstrapping multiple Angular applications only, you can skip most of them.

The Shared Service and the Shared Component

In order to demonstrate that different bootstraps can share common code, I created a simple service and a simple component.

JavaScript
import { Injectable } from '@angular/core';
    
@Injectable()
export class MathService {
    public AddOne(n) {
        return n + 1;
    }
}

The "math-service.ts" performs a simple math operation. It will be used in all the components to demonstrate that the code of an Angular service can be shared among components bootstrapped separately.

JavaScript
import { Component, DoCheck } from '@angular/core';
import { MathService } from '../../services/math-service';
    
@Component({
    selector: 'shared-sub-component',
    template: '<div class="component">' +
    '<h3>Shared Component</h3>' +
    '<div><button (click)="Add()">Click to add Count</button>' +
    '&nbsp;-&nbsp;{{Count}}</div>' +
    '</div>'
})
    
export class SharedSubComponent implements DoCheck {
    public Count = 0;
    constructor(private math: MathService) { }
    
    public ngDoCheck() {
        console.log('Shared Change detection!')
    }
    
    public Add() {
        this.Count = this.math.AddOne(this.Count);
    }
}

The "shared-sub-component.ts" implements an Angular component. It will be used as a sub-component by the other components to demonstrate that an Angular component can be shared among components bootstrapped separately.

The Angular App No.1

In this note, I will bootstrap two Angular applications on the same web page. The first application is implemented in the "app1" folder.

Image 2

The "component-1.ts" implements the application's only component.

JavaScript
import { Component, DoCheck } from '@angular/core';
import { MathService } from '../../services/math-service';
    
@Component({
    selector: 'component-1',
    template: '<div class="component">' +
    '<h3>Component-1</h3>' +
    '<div><button (click)="Add()">Click to add Count</button>' +
    '&nbsp;-&nbsp;{{Count}}</div>' +
    '<shared-sub-component></shared-sub-component>' +
    '</div>'
})
    
export class Component1 implements DoCheck {
    public Count = 0;
    constructor(private math: MathService) { }
    
    public ngDoCheck() {
        console.log('component-1 Change detection!')
    }
    
    public Add() {
        this.Count = this.math.AddOne(this.Count);
    }
}

The "component-1" components uses the "MathService" to make a simple math operation. It also uses the "shared-sub-component" as a sub-component. The "component-1" is packaged in the "app.module.ts" file.

JavaScript
import { NgModule }      from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { Component1 } from './components/component-1';
import { SharedSubComponent } from '../common/components/shared-sub-component';
    
import { MathService } from '../services/math-service';
    
@NgModule({
    imports: [BrowserModule],
    providers: [
        MathService
    ],
    declarations: [
        Component1,
        SharedSubComponent
    ],
    bootstrap: [
        Component1
    ]
})
    
export class AppModule { }

The "AppModule" will be bootstrapped through the "app1.ts" file.

JavaScript
import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app.module';
    
try { enableProdMode(); } catch(e) { }
const platform = platformBrowserDynamic();
    
platform.bootstrapModule(AppModule);

The Angular App No.2

Image 3

The second Angular application is implemented in the "app2" folder. It is virtually the same as the first application. You do not need to spend time to look at it. It is listed here just for completeness.

The "component-2.ts" component.

JavaScript
import { Component, DoCheck } from '@angular/core';
import { MathService } from '../../services/math-service';
    
@Component({
    selector: 'component-2',
    providers: [
        MathService
    ],
    template: '<div class="component">' +
    '<h3>Component-2</h3>' +
    '<div><button (click)="Add()">Click to add Count</button>' +
    '&nbsp;-&nbsp;{{Count}}</div>' +
    '<shared-sub-component></shared-sub-component>' +
    '</div>'
})
    
export class Component2 implements DoCheck {
    public Count = 0;
    constructor(private math: MathService) { }
    
    public ngDoCheck() {
        console.log('component-2 Change detection!')
    }
    
    public Add() {
        this.Count = this.math.AddOne(this.Count);
    }
}

The "app.module.ts" module.

JavaScript
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { Component2 } from './components/component-2';
import { SharedSubComponent } from '../common/components/shared-sub-component';
    
@NgModule({
    imports: [BrowserModule],
    declarations: [
        Component2,
        SharedSubComponent
    ],
    bootstrap: [
        Component2
    ]
})
    
export class AppModule { }

The "app2.ts" application.

JavaScript
import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app.module';
    
try { enableProdMode(); } catch (e) { }
const platform = platformBrowserDynamic();
    
platform.bootstrapModule(AppModule);

The "systemjs.config.js" File

In order to bootstrap multiple Angular applications in a single web page, I made some change to the "systemjs.config.js" file from the Angular official web page.

JavaScript
// http://plnkr.co/edit/aZqdJe3OZ8K2odHioWkB?p=info
    
let ng2_getstarter = function (option) {
    let configuration = {
        paths: { 'npm:': option.npm },
        map: {
            app: option.appPath,
            '@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',
    
            // other libraries
            'rxjs': 'npm:rxjs'
        },
        packages: {
            rxjs: {
                defaultExtension: 'js'
            },
            app: {
                defaultExtension: 'js'
            }
        }
    }
    
    System.config(configuration);
    
    return {
        bootstrap: function (appFile) {
            System.import(appFile).catch(function (err) { console.error(err);});
        }
    };
};
  • The "ng2_getstarter" function takes a JSON "option" object and returns an object to bootstrap the Angular applications.
  • The "option" object needs to provide the path to the "npm" folder and the path to the root folder of the Angular applications.

Boostrap the Applications

With the help of the "systemjs.config.js" file, bootstrapping multiple Angular applications in the same page is very simple. The "Index.cshtml" file shows how to use the "ng2_getstarter" function.

HTML
<body>
    <component-1></component-1>
    <component-2></component-2>
</body>

In the HTML section, we want to bootstrap both "component-1" and "component-2". The "component-1" is bundled in the "app1.ts" file and the "component-2" is bundled in the "app2.ts" file.

JavaScript
let ng_starter = ng2_getstarter({
    npm: '@Url.Content("~/node_modules/")',
    appPath: '@Url.Content("~/ngApp/")'
});
    
ng_starter.bootstrap('@Url.Content("~/ngApp/app1/app1.js")');
ng_starter.bootstrap('@Url.Content("~/ngApp/app2/app2.js")');
  • We can obtain a "ng_starter" object by passing the path to the "npm" folder and the path to the root of the Angular applications to the "ng2_getstarter" function.
  • To bootstrap the applications, we can simply call the "boostrap()" function on the "ng_starter" object by passing the path to each of the Angular application files.

The "TSC" Error

Image 4

Depending on the Typescript compiler installed in your Visual Studio, you may encounter a problem to build the application. If you encounter the above error, it is very likely that you have a mis-matched version of Typescript compiler. You can take a look at my early post to figure out the solutions.

Build and Load the Web Page

If everything goes well, you can compile and load the web page into the browser.

Image 5

You can see both Angular applications bootstrapped well in the same page. You can click on the buttons to check if they function well.

Image 6

If you open the developer tool and take a look at the console, you can see that the button clicks in each application only trigger change detections within the same application. It should provide some performance advantages if you have no intention to allow the actions in one application to update the state of the other applications.

Points of Interest

  • This is a note on how to bootstrap multiple apps in a single HTML page.
  • It is not always necessary to bootstrap multiple applications in the same page, but it may be of some advantages under certain conditions.
  • I hope you like my postings and I hope this note can help you one way or the other.

History

  • 3/5/2017: First revision

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)