I have a recursive tree structure containing nodes which each have an 'htmlStringContent' property. When I display the tree using nested 'node' components and try to present the html content I use:
<div [innerHtml]="node.htmlStringContent"></div>
The HTML displays correctly but for the following elements:
<a (click)="function()">click me</a>
The (click) functions don't work. I know this has previously been posted but with the large amount of updates angular has brought out recently I cant find any solutions. This answer leads me to believe I should be using the ngComponentOutlet directive but I'm not sure how..
How can I get angular to bind this click functionality?
Edit: I have been told to use the ComponentFactoryResolver but can't see how I can use this to display the html correctly. Can anyone provide further help?
Edit2: I am parsing 'htmlStringContent' through a sanitizing pipe before displaying it on [innerHtml]
transform(v: string) : SafeHtml {
return this._sanitizer.bypassSecurityTrustHtml(v);
}
Edit3: Basically this question is asking whether it is as all possible to display HTML from a property on an object in angular 2/ionic 2 while retaining the (click) functionality on it. I am also open to workaround answers.
CFR DEMO : https://plnkr.co/edit/jKEaDz1JVFoAw0YfOXEU?p=preview
@Component({
selector: 'my-app',
template: `
<button (click)="addComponents()">Add HTML (dynamically using CRF)</button>
<h1>Angular2 AppComponent</h1>
<hr>
<div>
<h5>dynamic html goes here</h5>
<div class="container">
<template #subContainer1></template>
</div>
</div>
`,
})
export class App {
name:string;
@ViewChild('subContainer1', {read: ViewContainerRef}) subContainer1: ViewContainerRef;
constructor(
private compFactoryResolver: ComponentFactoryResolver
) {
this.name = 'Angular2'
}
addComponents() {
let compFactory: ComponentFactory;
compFactory = this.compFactoryResolver.resolveComponentFactory(Part1Component);
this.subContainer1.createComponent(compFactory);
}
}
If I understand correctly, you need to use dynamic templates and compile them on runtime. If so, then you need to use the angular compiler:
@Component({
selector: 'my-app',
template: `
<h1>Angular 2 Dynamic Component</h1>
<template #container></template>
`
})
export class AppComponent implements AfterContentInit, OnDestroy {
private dynamicComponentRefs: ComponentRef[] = [];
@ViewChild('container', { read: ViewContainerRef }) container: ViewContainerRef;
constructor(private resolver: ComponentFactoryResolver,
private compiler: Compiler) {}
ngAfterContentInit() {
let html = `
<div>Always Visible</div>
<div [hidden]="clause1">Hidden because of clause1 = true</div>
<div [hidden]="clause2">Visible because of clause2 = false</div>
<button type="button" (click)="buttonClicked()">Click me!</button>
<div *ngIf="clicked">You clicked the button!</div>
`;
this.compiler.compileModuleAndAllComponentsAsync(createDynamicComponent(html))
.then((mwcf: ModuleWithComponentFactories) => {
let factory: ComponentFactory = mwcf.componentFactories.find(cf =>
cf.componentType.name === 'DynamicComponent');
this.dynamicComponentRefs
.push(this.container.createComponent(factory));
});
}
ngOnDestroy() {
/* Make sure you destroy all dynamically created components to avoid leaks */
this.dynamicComponentRefs.forEach(dcr => {
dcr.destroy();
});
}
}
export function createDynamicComponent(html: string): Type<NgModule> {
@Component({
template: html,
})
class DynamicComponent {
private clause1: boolean = true;
private clause2: boolean = false;
private clicked = false;
buttonClicked() {
this.clicked = true;
}
}
@NgModule({
imports: [CommonModule],
declarations: [DynamicComponent],
})
class DynamicComponentModule {}
return DynamicComponentModule;
}
Basically you need to dynamically create a component and the module that declares it (e.g. through a function) and pass it the template as an argument. Then you can call compileModuleAndAllComponentsAsync()
on the module and get the component factory you need. Then it's a matter of rendering it in the DOM through the ViewContainerRef.createComponent()
method.
Here is working plunker: dynamic template component
Keep in mind however that - for the moment - this approach can only be used with JIT compilation. In AOT compilation the angular compiler is not available and trying to use it will throw an error.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With