Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Bootstrap Angular App Multiple Times on Same Page

Tags:

angular

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);
like image 971
Josh Avatar asked Feb 16 '26 00:02

Josh


1 Answers

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>
like image 51
Josh Avatar answered Feb 17 '26 14:02

Josh



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!