I have a directive that reads src attribute and appends a DynamicComponent. It also sets an input property of DynamicComponent.
@Directive({
selector: '[appDynamic]'
})
export class InjectDirective {
@ViewChild('container', {read: ViewContainerRef}) container: ViewContainerRef;
constructor(
private element: ElementRef,
private viewContainerRef: ViewContainerRef,
private componentFactoryResolver: ComponentFactoryResolver
) { }
ngOnInit(){
const nativeElement = this.element.nativeElement;
const src = nativeElement.getAttribute('src');
if (src) {
const componentFactory = this.componentFactoryResolver.resolveComponentFactory(DynamicComponent);
const componentRef = this.viewContainerRef.createComponent(componentFactory);
componentRef.instance.mediaPath = src;
const parent = this.element.nativeElement.parentElement;
parent.insertBefore(componentRef.location.nativeElement, this.element.nativeElement.nextSibling)
}
}
}
The injection happens, but I can't read mediaPath
in the DynamicComponent
due to this
not being set to the right value. this
is an object that only has property __ngContext__
.
export class DynamicComponent implements OnInit {
@Input() mediaPath:string;
constructor() { }
ngOnInit(): void {
}
openSection(event){ // click event
console.log(this); // this does not refer to instance of DynamicComponent
console.log(this.mediaPath); // this.mediaPath is undefined
}
}
What is the correct way of creating a component dynamically and also passing values to the component properties? I am using angular 11.
UPDATED to Angular 14. Still the same problem:
@Directive({
selector: '[appInject]'
})
export class InjectDirective {
@ViewChild('container', {read: ViewContainerRef}) container: ViewContainerRef;
constructor(
private element: ElementRef,
private viewContainerRef: ViewContainerRef,
) { }
ngOnInit(){
const nativeElement = this.element.nativeElement;
const src = nativeElement.getAttribute('src');
if (src) {
const componentRef = this.viewContainerRef.createComponent(DynamicComponent);
componentRef.setInput("mediaPath",src)
const parent = this.element.nativeElement.parentElement;
parent.insertBefore(componentRef.location.nativeElement, this.element.nativeElement.nextSibling)
}
}
}
export class DynamicComponent implements OnInit {
@Input() mediaPath:string;
constructor() { }
ngOnInit(): void {}
openFindSimilarSection(event){
console.log(this); // only has __ngContext__ property
console.log(this.mediaPath); // this is undefined
}
}
//used like this:
<img [src]="dynamically.added.path" appInjectFindSimilar>
Try using setInput
method to pass in the input.
const componentRef =
this.viewContainerRef.createComponent(componentFactory);
componentRef.setInput('mediaPath', src);
import {
Component,
Input,
Directive,
ViewChild,
ViewContainerRef,
ComponentFactoryResolver,
ElementRef,
Renderer2,
Attribute,
} from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';
@Component({
selector: 'dynamic-component',
template: `dynamic component`,
})
export class DynamicComponent {
@Input() mediaPath!: string;
constructor() {}
ngOnInit(): void {
// click event
console.log(this); // this does not refer to instance of DynamicComponent
console.log(this.mediaPath); // this.mediaPath is undefined
}
openSection(event: any) {}
}
@Directive({
selector: '[appDynamic]',
})
export class InjectDirective {
@ViewChild('container', { read: ViewContainerRef })
container!: ViewContainerRef;
constructor(
private element: ElementRef,
private viewContainerRef: ViewContainerRef,
private componentFactoryResolver: ComponentFactoryResolver,
private renderer: Renderer2,
@Attribute('src') public src: string
) {}
ngOnInit() {
const nativeElement = this.element.nativeElement;
if (this.src) {
const componentFactory =
this.componentFactoryResolver.resolveComponentFactory(DynamicComponent);
const componentRef =
this.viewContainerRef.createComponent(componentFactory);
componentRef.setInput('mediaPath', this.src);
// componentRef.instance.mediaPath = src;
const parent = this.element.nativeElement.parentElement;
parent.insertBefore(
componentRef.location.nativeElement,
this.element.nativeElement.nextSibling
);
}
}
}
@Component({
selector: 'app-root',
imports: [InjectDirective],
template: `
<img appDynamic [src]="'test'"/>
`,
})
export class App {
name = 'Angular';
}
bootstrapApplication(App);