Techminded

Angular 2 dynamic bootstrap

Sometimes you’re not able to bind framework as the root page element as some html/js infrastructure already exists or you just have outside content rendered with other technologies. But you still can gain benefits of frameworks development and get functionality from subset of well-designed and working components. Angular 2 is more modular than predecessors and allow use to only some parts or it.


We will start with this repo of Angular 2 app skeleton build with babel transpiler that will allow us to use modern JavaScript and main language:


https://github.com/shuhei/babel-angular2-app


So in case you need to render and bind component to some custom area, e.g. that is loaded dynamically and/or shown in overlay you can have the following dynamic component rendered instead of default app:


app.js: https://gist.github.com/syncro/9587abc73fe7f8e57f9a78d3a2646983

 

import {
  Component,
  DynamicComponentLoader,
  Injector,
  ViewContainerRef
} from '@angular/core';

import {SomeComponent} from './components/somecomponent/somecomponent.component.js';

var COMPONENTS = {'somecomponent': SomeComponent};

@Component({
  selector: 'app',
  directives: [SomeComponent],
  template: `
    <somecomponent></somecomponent>
  `
})
export class App {

  constructor(dcl: DynamicComponentLoader, injector: Injector, viewContainerRef: ViewContainerRef) {
    this.dcl = dcl;
    this.injector = injector;
    this.viewContainerRef = viewContainerRef;
  }

  ngOnInit() {
    this.bindComponentAddEvent();
  }

  bindComponentAddEvent() {
    window.addEventListener('component:add', function (e) {
      var cmp = COMPONENTS[e.detail.component];
      var sl = e.detail.selector;
      console.debug('Loading component: ' + e.detail.component + ', to: ' + sl);
      //this.dcl.loadAsRoot(cmp, sl,this.injector);
      this.dcl.loadNextToLocation(cmp, sl);
    }.bind(this), false);
  }
}

your will also need index.js that can also do bootstrap triggered by event


Index.js: https://gist.github.com/syncro/9587abc73fe7f8e57f9a78d3a2646983

 

import {
  Component,
  DynamicComponentLoader,
  Injector,
  ViewContainerRef
} from '@angular/core';

import {SomeComponent} from './components/somecomponent/somecomponent.component.js';

var COMPONENTS = {'somecomponent': SomeComponent};

@Component({
  selector: 'app',
  directives: [SomeComponent],
  template: `
    <somecomponent></somecomponent>
  `
})
export class App {

  constructor(dcl: DynamicComponentLoader, injector: Injector, viewContainerRef: ViewContainerRef) {
    this.dcl = dcl;
    this.injector = injector;
    this.viewContainerRef = viewContainerRef;
  }

  ngOnInit() {
    this.bindComponentAddEvent();
  }

  bindComponentAddEvent() {
    window.addEventListener('component:add', function (e) {
      var cmp = COMPONENTS[e.detail.component];
      var sl = e.detail.selector;
      console.debug('Loading component: ' + e.detail.component + ', to: ' + sl);
      //this.dcl.loadAsRoot(cmp, sl,this.injector);
      this.dcl.loadNextToLocation(cmp, sl);
    }.bind(this), false);
  }
}

Than you can trigger events from anywere of your javascript code:

 

window.dispatchEvent(new CustomEvent('bootstrap', {
   detail: {
	
   }
}));

or

window.dispatchEvent(new CustomEvent('component:add', {
   detail: {
	component: ‘somecomponent’,
	sl: ‘selector’ // e.g. you have div with id #someComponent
   }
}));

You can even pass some references from global to have control over them inside Angular app even assuming it running in zone with one-way access to externals.

Comments