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