Dynamic Components with Angular and jQWidgets

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.zip

The 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": {}
}
        

Angular Grid with Dynamic jqxButton Components


A video tutorial is also available on YouTube: Tutorial: QuickStart Angular project with jQWidgets.