Reputation: 1276
Can someone help me, how to use ng-template inside innerHTML tag,
Stackblitz Demo: https://stackblitz.com/edit/angular-xgnnj4
What I wanted to do is,
In the Component HTML
<div [innerHTML]="htmlString"><div>
In the Component TS
@ViewChild('dynamicComponent', { static: false, read: ViewContainerRef }) myRef: ViewContainerRef;
htmlString = `<ng-template #dynamicComponent></ng-template>`;
ngAfterViewInit(): void {
const factory = this.componentFactoryResolver.resolveComponentFactory(CommentComponent);
const ref = this.myRef.createComponent(factory);
ref.changeDetectorRef.detectChanges();
}
I really need to achieve this way because I wanted to get the HTML content from an API and use that value to display the HTML content dynamically
As I am getting the error as Cannot read property 'createComponent' of undefined but if I put outside the innerHTML it works perfectly
I have gone through https://angular.io/guide/dynamic-component-loader but it's not helping my scneario
Upvotes: 0
Views: 4992
Reputation: 1905
What you are basically trying to do is dynamically load a component inside a dynamically loaded string. Any template either provided inline or separately in an HTML file is processed by the Angular rendering engine. (Ivy is the new and optimized rendering engine starting from Angular 9, from Angular 4 till 8 default rendering engine is called Renderer2
which is the successor to Renderer
which was the default from Angular 2 till 4).
This is basically what you are trying to do in attached stackBlitz,
@ViewChild("dynamicComponent", { static: false, read: ViewContainerRef }) myRef: ViewContainerRef;
constructor(private componentFactoryResolver: ComponentFactoryResolver) {
//Simulate the network call
setTimeout(() => {
this.htmlChildString = `<ng-template #dynamicComponent></ng-template>`;
}, 2000);
}
ngAfterViewInit(): void {
const factory = this.componentFactoryResolver.resolveComponentFactory(
ChildViewComponent
);
const ref = this.myRef.createComponent(factory);
ref.changeDetectorRef.detectChanges();
}
first thing which is wrong here is that obviously ngAfterViewInit
will be called before your setTimeout
method written in the constructor
due to your defined timeout of 2000ms
, so myRef
will always be undefined because its not yet instantiated, next issue which I see is regarding your template,
<div [innerHTML]="htmlChildString"></div>
innerHTML
is not supposed to contain any angular symbols, it can only contain plain HTML which can be directly embedded in the DOM since they won't be processed by the Angular rendering engine, which is exactly what @Andriy has already pointed out.
Now coming to the reason why what you are attempting is not possible, Suppose you define a component named AComponent
having template:
<span>I am {{name}}</span>
angular compiler, when going through each of your (statically defined) components and templates attached to these component, it creates factory for each component. When angular compiler goes through above template it generates a factory like following,
function View_AComponent_0(l) {
return jit_viewDef1(0,
[
jit_elementDef2(0,null,null,1,'span',...),
jit_textDef3(null,['I am ',...])
],
null,
function(_ck,_v) {
var _co = _v.component;
var currVal_0 = _co.name;
_ck(_v,1,0,currVal_0);
notice following line?
jit_elementDef2(0,null,null,1,'span',...),
What Angular is doing is using JS to create elements, Angular uses this factory to instantiate View Definition which in turn is used to create component View. Under the hood Angular represents an application as a tree of views. There is only one instance of a view definition per component type which acts as a template for all views. But for each component instance Angular creates a separate view.
Above factory describes the structure of a component view and is used when instantiating the component. The jit_viewDef1
is the reference to the viewDef
function that creates a view definition.
View definition receives view definition nodes as parameters which resemble the structure of the html but also contain many angular specific details.
this factory is what is returned from resolveComponentFactory
method, since you have statically declared ChildViewComponent
in your module and also marked it as an entryComponent
, that's the reason angular decided to pre generate its factory even though it knows its not currently being used by Router or in another component's template.
Similarly ViewContainerRef
is a reference to a view container (ng-template
or ng-container
) which has to be either statically defined directly in the template or which should be be encounter able via a combination of ngSwitch
or ngIf
directives, but in any case compiler / rendering engine has to be aware of the presence of this container before hand while compiling.
In your case when you load over these angular symbols dynamically via API, angular only knows this is a simple string and doesn't know before hand that this is some thing which can contain a view at run time. If you ever had a look at the build generated by Angular, it only contain JS files and no HTML template or CSS styling files corresponding to your components. Every template contained with in those HTML files gets transformed into factory methods which are eventually used at run time.
What I would recommend you is to,
resolveComponentFactory
and createComponent
and a statically defined templateRef
which is dynamically loaded in the viewContainerRef
on the click of a button.ChildViewComponent
in there and have an Iframe load up the content based on any condition if required,Upvotes: 5
Reputation: 15442
You can use DomSanitizer to pass any HTML to template:
import { DomSanitizer } from '@angular/platform-browser';
...
export class HelloComponent {
htmlString = this.sanitizer.bypassSecurityTrustHtml(`
<ng-template>
<div style="width: 10px; height: 10px; background: red;"></div>
</ng-template>
`);
constructor(private sanitizer: DomSanitizer) {}
}
STACKBLITZ: https://stackblitz.com/edit/angular-t9roxg?file=src%2Fapp%2Fhello.component.ts
keep in mind that in this way HTML is passed as plain HTML without any Angular processing
Upvotes: 2