Load and Compile Dynamical HTML in AngularJS
We have to load HTML content with CSS from the backend dynamically and compile it in an Angular application. We also need to support Angular directives defined in HTML.
Join the DZone community and get the full member experience.
Join For FreeAs a project requirement, we have to load HTML content with CSS (in JSON response) from the backend dynamically and compile it in an Angular application. We also need to support those Angular directives defined in HTML. This article will describe the solution for both AngularJS and Angular.
AngularJS
To compile dynamical HTML in AngularJS, we just need to simply inject $compile
and use it to compile the HTML after injecting. So here we use a JQuery function to fetch HTML elements and set HTML into it.
sample1.json
"content": ""<body width=\"100%\" class=\"height100 body\">\n<a name=\"N10001\"></a>\n<table directive1 data-elename=\"dmodule\" width=\"100%\" border=\"0\" frame=\"all\">\n<tbody><tr>\n<td",
"css" : ".isDisabled { cursor: not-allowed; opacity: 0.5; text-decoration: none;pointer-events: none; } .deleted { background-position: 0px 0px }"
xxxxxxxxxx
$http.get("data/sample1.json").then(function (response) {
var content = response.data;
var cssContent = content.css;
var htmlContent = content.content;
$timeout(function () {
var tabContentElem = $("#html-content");
tabContentElem.html("<div><style>" + cssContent + "</style>" + htmlContent + "</div>").promise().done(function () {
$compile(tabContentElem.contents())($scope);
});
},500);
})
And define directive like below,
xxxxxxxxxx
myapp.directive('directive1', function ($window) {
return {
restrict: 'A',
scope: true,
link: function (scope, element, attr) {
//behavior in directive
}
};
})
.directive('directive2', function ($window) {
return {
restrict: 'C',
scope: true,
link: function (scope, element, attr) {
//behavior in directive
element.on('click', function() {
});
}
};
Angular 10+
Now, let’s talk about implementation on Angular. The codes list below run under Angular 10.
To compile dynamical HTML, we need to install package @angular/platform-browser-dynamic
to install the JIT compiler manually.
xxxxxxxxxx
"dependencies": {
"@angular/animations": "~10.0.3",
"@angular/common": "~10.0.3",
"@angular/compiler": "~10.0.3",
"@angular/core": "~10.0.3",
"@angular/forms": "~10.0.3",
"@angular/platform-browser": "~10.0.3",
"@angular/platform-browser-dynamic": "^10.0.14",
app.module.ts
xxxxxxxxxx
import { NgModule, COMPILER_OPTIONS, CompilerFactory, Compiler} from '@angular/core';
import { JitCompilerFactory } from '@angular/platform-browser-dynamic';
providers: [
{ provide: COMPILER_OPTIONS, useValue: {}, multi: true },
{ provide: CompilerFactory, useClass: JitCompilerFactory, deps: [COMPILER_OPTIONS] },
{ provide: Compiler, useFactory: createCompiler, deps: [CompilerFactory] },
],
export class AppModule { }
export function createCompiler(compilerFactory: CompilerFactory): any{
return compilerFactory.createCompiler();
}
Then in the parent component which wants to create a sub-component and show/compile dynamical HTML. In component, we create a sub-component and put HTML and CSS data in it, and cast it to dynamicalComponent
.
xxxxxxxxxx
@ViewChild('container', { read: ViewContainerRef }) container: ViewContainerRef;
private async generateUserDynamicComponent() {
// Define the component using Component decorator.
const component = Component({
template: this.htmlData,
styles: [this.cssData]
})(dynamicalComponent);
const componentFactory = await this.dynamicComponentService.generateDynamic(component);
// Create the component and add to the view.
const componentRef = this.container.createComponent(componentFactory);
// Assign the service to the component instance
componentRef.instance.dataService = this.dataService;
this.dynamicComponent = componentRef;
this.container.insert(componentRef.hostView);
}
x
<ng-container #container>
</ng-container>
dynamic-component.service:
xxxxxxxxxx
async generateDynamic(component: any): Promise<ComponentFactory<any>> {
this.compiler.clearCache();
const type = component;
const module = this.generateNewModule(type);
return new Promise(
(resolve, reject) => {
this.compiler.compileModuleAndAllComponentsAsync(module)
.then((factories) => {
const componentFactory = factories.componentFactories[0];
resolve(componentFactory);
},
(error) => reject(error));
});
}
private generateNewModule(componentType: any): any {
// Define the module using NgModule decorator.
const module = NgModule({
declarations: [componentType, directive1, directive2],
imports: [CommonModule],
})(class { });
return module;
}
Then we can define dynamicalComponent
with functions:
xxxxxxxxxx
import { Directive, OnDestroy, OnInit, ViewChild } from '@angular/core';
@Directive({
selector: 'dynamic'
}
constructor() { }
ngOnInit() {}
ngOnDestroy() {
console.log('Dynamic Component OnDestroy')
}
It should be noticed when a module is created dynamically 'declarations: [componentType, directive1, directive2],' 'directive1,' 'directive2' are included. That is how while dynamical HTML compiled, those predefined directives embedded in HTML can be triggered. For example: directive1
xxxxxxxxxx
@Directive({
selector: '[directive1]'
})
export class DynamicDirective1 {
el: ElementRef;
constructor(el: ElementRef) {
this.el = el;
}
@HostBinding('class')
elementClass = 'newclass';
@HostListener('click', ['$event.target'])
onClick(): void {
}
See demo: https://github.com/unicorn82/angular-dynamic-html-component
Opinions expressed by DZone contributors are their own.
Comments