Skip to content

Instantly share code, notes, and snippets.

@benjamincharity
Created April 28, 2017 14:49
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save benjamincharity/8116414c7f38cffe3cef0e44fe44295d to your computer and use it in GitHub Desktop.
Save benjamincharity/8116414c7f38cffe3cef0e44fe44295d to your computer and use it in GitHub Desktop.
A directive to dynamically compile HTML. Source: https://plnkr.co/edit/mcvILwmOLvrS2PxIrXX8?p=preview
import {
Component,
Directive,
NgModule,
Input,
ViewContainerRef,
Compiler,
ComponentFactory,
ModuleWithComponentFactories,
ComponentRef,
ReflectiveInjector
} from '@angular/core';
import { RouterModule } from '@angular/router';
import { CommonModule } from '@angular/common';
export function createComponentFactory(compiler: Compiler, metadata: Component): Promise<ComponentFactory<any>> {
const cmpClass = class DynamicComponent {};
const decoratedCmp = Component(metadata)(cmpClass);
@NgModule({ imports: [CommonModule, RouterModule], declarations: [decoratedCmp] })
class DynamicHtmlModule { }
return compiler.compileModuleAndAllComponentsAsync(DynamicHtmlModule)
.then((moduleWithComponentFactory: ModuleWithComponentFactories<any>) => {
return moduleWithComponentFactory.componentFactories
.find(x => x.componentType === decoratedCmp);
});
}
@Directive({
selector: 'client-html-outlet',
})
export class ClientHtmlOutletDirective {
@Input() html: string;
cmpRef: ComponentRef<any>;
constructor(private vcRef: ViewContainerRef, private compiler: Compiler) { }
ngOnChanges() {
const html = this.html;
if (!html) return;
if (this.cmpRef) {
this.cmpRef.destroy();
}
const compMetadata = new Component({
selector: 'dynamic-html',
template: this.html,
});
createComponentFactory(this.compiler, compMetadata).then(factory => {
const injector = ReflectiveInjector.fromResolvedProviders([], this.vcRef.parentInjector);
this.cmpRef = this.vcRef.createComponent(factory, 0, injector, []);
});
}
ngOnDestroy() {
if (this.cmpRef) {
this.cmpRef.destroy();
}
}
}
@jesteross
Copy link

Hi Benjamin
I use your html outlet directive in my project.
Thank you a lot for this!

Could you please explain me one thing
If dynamic html will contain some methods
<a (click)='onClick()'>home

Do I have possibility to predefine and describe this method somewhere in APP?

Thanks!

@benjamincharity
Copy link
Author

Sorry, I never saw this comment..

I haven't had the need for dynamic component generation in quite some time so I'm a bit outdated on this side of things. Possibly something like this could work. https://stackoverflow.com/a/49038739/722367 … Basically use the reference once you create the component to dynamically add methods/functionality

@chxylucy
Copy link

Hi Benjamin
I use your html outlet directive in my project.
Thank you a lot for this!

Could you please explain me one thing
If dynamic html will contain some methods
<a (click)='onClick()'>home

Do I have possibility to predefine and describe this method somewhere in APP?

Thanks!

See my updated code here

https://stackoverflow.com/questions/56279217/angular-dynamic-html-template-component-with-event-emitter/

inside the dynamic html you can pass an event trigger string back to the parent component to throw into a switch in order to control the behaviour

inside the dynamic html use

(click)="genEmit('pass back to parent')

inside the parent that holds the outlet

you can capture the emitted event

<html-outlet [html]="tableHTMLOutput" (genericEmitter)="captureEvent($event)" ></html-outlet>

@Oztrea
Copy link

Oztrea commented Sep 23, 2020

I'm trying to use your directive to include a mvc partial view in my angular. But it doesn't work with Angular 10. Any Idea what's wrong?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment