Component templates are not always fixed. An application may need to load new components at runtime. In order to load components dynamically, Angular's ComponentFactoryResolver will be used.
The full source for the final version of the app can be downloaded from angular-dynamic-components.zipThe following example shows how to build a Grid with Dynamic components loaded into the Grid cells.
app.module.ts
import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { AppComponent } from './app.component'; import { jqxDomService } from './jqwidgets-dom.service'; import { jqxGridComponent} from 'jqwidgets-ng/jqxgrid'; import { jqxButtonComponent} from 'jqwidgets-ng/jqxbuttons'; @NgModule({ imports: [ BrowserModule ], declarations: [ AppComponent, jqxGridComponent, jqxButtonComponent ], entryComponents: [jqxButtonComponent], providers: [ jqxDomService ], bootstrap: [ AppComponent ] }) export class AppModule { // Diagnostic only: inspect router configuration constructor() { } }
jqwidgets-dom.service.ts
import { Injectable, Injector, EmbeddedViewRef, ComponentFactoryResolver, ApplicationRef } from '@angular/core'; @Injectable() export class jqxDomService { componentRef: any; constructor( private componentFactoryResolver: ComponentFactoryResolver, private appRef: ApplicationRef, private injector: Injector ) { } loadComponent(component: any, ownerElement: any) { // 1. Create a component reference from the component const componentRef = this.componentFactoryResolver .resolveComponentFactory(component) .create(this.injector, ownerElement); // 2. Attach component to the appRef so that it's inside the ng component tree this.appRef.attachView(componentRef.hostView); // 3. Get DOM element from component const domElement = (componentRef.hostView as EmbeddedViewRef) .rootNodes[0] as HTMLElement; if (ownerElement) { ownerElement.appendChild(domElement); } this.componentRef = componentRef; return {componentRef: componentRef, domElement: domElement } } destroy() { this.appRef.detachView(this.componentRef.hostView); this.componentRef.destroy(); } }
app.components.ts
import { jqxDomService } from './jqwidgets-dom.service'; import { Component, OnInit, EventEmitter } from '@angular/core'; import { jqxButtonComponent } from 'jqwidgets-ng/jqxbuttons'; export class Hero { id: number; name: string; } @Component({ selector: 'app-root', template: ` <h1 class="title">Angular Dynamic Component Loader</h1> <jqxGrid #grid [autoheight]="true" [theme]="'metro'" [columns]=columns> <tr> <th>Id</th> <th>Name</th> </tr> <tr *ngFor="let hero of HEROES"> <td> {{hero.id}}</td> <td> {{hero.name}}</td> </tr> </jqxGrid> {{clickMessage}} ` }) export class AppComponent { HEROES: Hero[] = [ { id: 11, name: 'Mr. Nice' }, { id: 12, name: 'Narco' }, { id: 13, name: 'Bombasto' }, { id: 14, name: 'Celeritas' }, { id: 15, name: 'Magneta' }, { id: 16, name: 'RubberMan' }, { id: 17, name: 'Dynama' }, { id: 18, name: 'Dr IQ' }, { id: 19, name: 'Magma' }, { id: 20, name: 'Tornado' } ] clickMessage = ''; onClickMe(event) { this.clickMessage = 'You are my hero ' + event.target.textContent; } constructor(private jqxDomService: jqxDomService) { } ngOnInit() { } columns: any[] = [ { text: 'Id', width: 110, datafield: 'Id', createwidget: (row: number, column: any, value: string, htmlElement: HTMLElement): void => { const that = this; let container = document.createElement('div'); htmlElement.appendChild(container); let result = this.jqxDomService.loadComponent(jqxButtonComponent, container); (<jqxButtonComponent>result.componentRef.instance).autoCreate = false; (<jqxButtonComponent>result.componentRef.instance).onClick.subscribe((clickEvent, value) => { that.onClickMe(clickEvent); }); (<jqxButtonComponent>result.componentRef.instance).createComponent({value: value, width: 100}); }, initwidget: (row: number, column: any, value: any, htmlElement: HTMLElement): void => { } }, { text: 'Name', datafield: 'Name' } ]; }
package.json
{ "name": "angular-io-example", "version": "1.0.0", "private": true, "description": "Example project from an angular.io guide.", "scripts": { "ng": "ng", "build": "ng build --base-href ./ --prod", "start": "ng serve", "test": "ng test", "lint": "tslint ./src/**/*.ts -t verbose", "e2e": "ng e2e" }, "keywords": [], "author": "", "license": "https://www.jqwidgets.com/license/", "dependencies": { "@angular/animations": "~5.0.0", "@angular/common": "~5.0.0", "@angular/compiler": "~5.0.0", "@angular/compiler-cli": "~5.0.0", "@angular/core": "~5.0.0", "@angular/forms": "~5.0.0", "@angular/http": "~5.0.0", "@angular/platform-browser": "~5.0.0", "@angular/platform-browser-dynamic": "~5.0.0", "@angular/platform-server": "~5.0.0", "@angular/router": "~5.0.0", "@angular/upgrade": "~5.0.0", "jqwidgets-ng": "^9.1.1", "angular-in-memory-web-api": "~0.5.0", "core-js": "^2.4.1", "rxjs": "^5.5.0", "zone.js": "^0.8.4" }, "devDependencies": { "@angular/cli": "1.6.5", "@types/jasmine": "~2.8.0", "@types/jasminewd2": "^2.0.3", "@types/node": "^6.0.45", "jasmine-core": "~2.8.0", "jasmine-spec-reporter": "^4.2.1", "karma": "^1.3.0", "karma-chrome-launcher": "^2.0.0", "karma-cli": "^1.0.1", "karma-coverage-istanbul-reporter": "^1.3.3", "karma-jasmine": "^1.0.2", "karma-jasmine-html-reporter": "^0.2.2", "karma-phantomjs-launcher": "^1.0.2", "lodash": "^4.16.2", "phantomjs-prebuilt": "^2.1.7", "protractor": "~5.1.0", "ts-node": "^3.3.0", "tslint": "^3.15.1", "typescript": "2.4.2" }, "repository": {} }