I would like to bootstrap an Angular 4 micro app multiple times on the same page. Basically, foreach instance of class ‘.angular-micro-app’, bootstrap a new app instance.
I understand that traditionally these would be Angular components within a single parent App. In my case, that is not possible and I need multiple instances of the same root level app (component), on the same page. This was fairly trivial with AngularJS 1.x, but is proving rather frustrating with Angular.
Example:
index.html snippet:
<body>
<div class=“.angular-micro-app”></div>
…
<div class=“.angular-micro-app”></div> <!-- this element is NOT bootstrapping -->
</body>
app.component.ts:
import { Component } from '@angular/core';
@Component({
selector: '.angular-micro-app',
template: require('./app.component.html'),
})
export class AppComponent { }
app.module.ts:
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
],
providers: [
],
bootstrap: [AppComponent],
})
export class AppModule { }
In a main file, I’m doing the basic platform bootstrap:
platformBrowserDynamic().bootstrapModule(AppModule);
This can be accomplished by manually bootstrapping your root level component(s) in the NgModule ngDoBootstrap method.
(Note that in Angular 5+ this method may no longer be required, see this Angular PR)
We first find all root elements we want to bootstrap and give them a unique ID. Then for each instance, hack the component factory selector with the new ID and trigger the bootstrap.
const entryComponents = [
RootComponent,
];
@NgModule({
entryComponents,
imports: [
BrowserModule,
],
declarations: [
RootComponent,
],
})
export class MyModule {
constructor(private resolver: ComponentFactoryResolver) {}
ngDoBootstrap(appRef: ApplicationRef) {
entryComponents.forEach((component: any) => {
const factory = this.resolver.resolveComponentFactory(component);
let selectorName;
let elements;
// if selector is a class
if (factory.selector.startsWith('.')) {
selectorName = factory.selector.replace(/^\./, '');
elements = document.getElementsByClassName(selectorName);
// else assume selector is an element
} else {
selectorName = factory.selector;
elements = document.getElementsByTagName(selectorName);
}
// no elements found, early return
if (elements.length === 0) {
return;
}
// more than one root level componenet found, bootstrap unique instances
if (elements.length > 1) {
const originalSelector = factory.selector;
for (let i = 0; i < elements.length; i += 1) {
elements[i].id = selectorName + '_' + i;
(<any>factory).factory.selector = '#' + elements[i].id;
appRef.bootstrap(factory);
}
(<any>factory).factory.selector = originalSelector;
// only a single root level component found, bootstrap as usual
} else {
appRef.bootstrap(factory);
}
});
}
}
Now, assuming our RootComponent's selector was '.angular-micro-app' this will work as expected:
<body>
<div class="angular-micro-app"></div>
...
<div class="angular-micro-app"></div>
</body>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With