I have defined several components, including Textbox, Autocomplete, and Button, and I want to use them in another component. My goal is to dynamically load a component in another component based on its name. To achieve this, I wrote the following code:
I created a directive as follows:
import { Directive, ViewContainerRef } from '@angular/core';
@Directive({
selector: '[appDynamicHost]',
standalone: true
})
export class DynamicHostDirective {
constructor(public viewContainerRef: ViewContainerRef) { }
}
I wrote a service as follows:
import { Injectable, Type } from '@angular/core';
import { PngAutoCompleteComponent } from '@components/components/png-auto-complete/png-auto-completeponent';
import { PngButtonComponent } from '@components/components/png-button/png-buttonponent';
import { PngTextboxComponent } from '@components/components/png-textbox/png-textboxponent';
@Injectable({
providedIn: 'root'
})
export class DynamicComponentService {
constructor() { }
private components: { [key: string]: Type<any> } = {
'pngTextbox': PngTextboxComponent,
'pngButton': PngButtonComponent,
'pngAutoComplete':PngAutoCompleteComponent
};
getComponent(name: string): Type<any> | null {
return thisponents[name] || null;
}
}
I implemented a dynamic component loader as follows:
.ts file :
import { Component, ComponentRef, ViewChild } from '@angular/core';
import { DynamicComponentService } from '@services/dynamic-component.service';
import { DynamicHostDirective } from 'src/app/directives/dynamic-host.directive';
@Component({
selector: 'app-dynamic-loader',
standalone: true,
imports: [],
templateUrl: './dynamic-loaderponent.html',
styleUrl: './dynamic-loaderponent.scss'
})
export class DynamicLoaderComponent {
@ViewChild(DynamicHostDirective, { static: true }) dynamicHost!: DynamicHostDirective;
private componentRef!: ComponentRef<any>;
constructor(private dynamicService: DynamicComponentService) {
}
loadComponent(componentName: string, data: any) {
const componentType = this.dynamicService.getComponent(componentName);
if (!componentType) {
console.error(`Component "${componentName}" not found!`);
return;
}
const viewContainerRef = this.dynamicHost.viewContainerRef;
viewContainerRef.clear();
thisponentRef = viewContainerRef.createComponent(componentType);
if (thisponentRef.instance) {
thisponentRef.instance.data = data;
}
}
getComponentData() {
if (thisponentRef && thisponentRef.instance) {
return thisponentRef.instance.data;
}
return null;
}
ngOnInit() {}
}
html file:
<ng-template #dynamicHost></ng-template>
Finally, I called the dynamic component loader in the main component.
.ts file:
import { Component, OnInit, ViewChild } from '@angular/core';
import { DynamicLoaderComponent } from '@components/components/dynamic-loader/dynamic-loaderponent';
@Component({
selector: 'app-form-001',
standalone: true,
imports: [DynamicLoaderComponent],
templateUrl: './form-001ponent.html',
styleUrl: './form-001ponent.scss'
})
export class Form001Component implements OnInit{
@ViewChild('dynamicLoaderx') dynamicLoader!: DynamicLoaderComponent;
// constructor (private dynamicLoader: DynamicLoaderComponent) {}
ngOnInit(): void {
this.dynamicLoader.loadComponent('pngTextbox', { invoiceNumber: '12345', date: '1402/12/01' });
}
}
.hrml file:
<app-dynamic-loader #dynamicLoaderx></app-dynamic-loader>
I am not sure what I did wrong, but I am getting the following error:
ERROR TypeError: Cannot read properties of undefined (reading 'loadComponent')
at _Form001Component.ngOnInit
I have defined several components, including Textbox, Autocomplete, and Button, and I want to use them in another component. My goal is to dynamically load a component in another component based on its name. To achieve this, I wrote the following code:
I created a directive as follows:
import { Directive, ViewContainerRef } from '@angular/core';
@Directive({
selector: '[appDynamicHost]',
standalone: true
})
export class DynamicHostDirective {
constructor(public viewContainerRef: ViewContainerRef) { }
}
I wrote a service as follows:
import { Injectable, Type } from '@angular/core';
import { PngAutoCompleteComponent } from '@components/components/png-auto-complete/png-auto-completeponent';
import { PngButtonComponent } from '@components/components/png-button/png-buttonponent';
import { PngTextboxComponent } from '@components/components/png-textbox/png-textboxponent';
@Injectable({
providedIn: 'root'
})
export class DynamicComponentService {
constructor() { }
private components: { [key: string]: Type<any> } = {
'pngTextbox': PngTextboxComponent,
'pngButton': PngButtonComponent,
'pngAutoComplete':PngAutoCompleteComponent
};
getComponent(name: string): Type<any> | null {
return thisponents[name] || null;
}
}
I implemented a dynamic component loader as follows:
.ts file :
import { Component, ComponentRef, ViewChild } from '@angular/core';
import { DynamicComponentService } from '@services/dynamic-component.service';
import { DynamicHostDirective } from 'src/app/directives/dynamic-host.directive';
@Component({
selector: 'app-dynamic-loader',
standalone: true,
imports: [],
templateUrl: './dynamic-loaderponent.html',
styleUrl: './dynamic-loaderponent.scss'
})
export class DynamicLoaderComponent {
@ViewChild(DynamicHostDirective, { static: true }) dynamicHost!: DynamicHostDirective;
private componentRef!: ComponentRef<any>;
constructor(private dynamicService: DynamicComponentService) {
}
loadComponent(componentName: string, data: any) {
const componentType = this.dynamicService.getComponent(componentName);
if (!componentType) {
console.error(`Component "${componentName}" not found!`);
return;
}
const viewContainerRef = this.dynamicHost.viewContainerRef;
viewContainerRef.clear();
thisponentRef = viewContainerRef.createComponent(componentType);
if (thisponentRef.instance) {
thisponentRef.instance.data = data;
}
}
getComponentData() {
if (thisponentRef && thisponentRef.instance) {
return thisponentRef.instance.data;
}
return null;
}
ngOnInit() {}
}
html file:
<ng-template #dynamicHost></ng-template>
Finally, I called the dynamic component loader in the main component.
.ts file:
import { Component, OnInit, ViewChild } from '@angular/core';
import { DynamicLoaderComponent } from '@components/components/dynamic-loader/dynamic-loaderponent';
@Component({
selector: 'app-form-001',
standalone: true,
imports: [DynamicLoaderComponent],
templateUrl: './form-001ponent.html',
styleUrl: './form-001ponent.scss'
})
export class Form001Component implements OnInit{
@ViewChild('dynamicLoaderx') dynamicLoader!: DynamicLoaderComponent;
// constructor (private dynamicLoader: DynamicLoaderComponent) {}
ngOnInit(): void {
this.dynamicLoader.loadComponent('pngTextbox', { invoiceNumber: '12345', date: '1402/12/01' });
}
}
.hrml file:
<app-dynamic-loader #dynamicLoaderx></app-dynamic-loader>
I am not sure what I did wrong, but I am getting the following error:
ERROR TypeError: Cannot read properties of undefined (reading 'loadComponent')
at _Form001Component.ngOnInit
Share
Improve this question
asked Feb 16 at 18:00
Blue MoonBlue Moon
4491 gold badge7 silver badges23 bronze badges
1 Answer
Reset to default 1The issue is in the timing. dynamicLoader is defined as ViewChild
and because of that is available only after the AfterViewInit
lifecycle hook and undefined during OnInit
. Just switch ngOnInit
to ngAfterViewInit
and you should be good to go.