javascript - Añadiendo componentes programáticamente en posiciones dinámicas en Angular
Mi punto de partida es algún tipo de fragmento HTML, que se carga en tiempo de ejecución desde mi backend, con etiquetas de marcador de posición:
<table>
<tbody>
<tr>
<td>
<span>Col1</span>
</td>
<td>
<span class="placeholderClass" data-placeholderdata="xy"></span>
</td>
</tr>
</tbody>
</table>
Quiero mostrar el fragmento de HTML pero reemplazar todos los elementos <span class="placeholderClass"/> con mi propio componente de Angular.
Hasta ahora leí sobre el cargador de componentes dinámicos de Angular, pero según entendí, esto solo me permite cargar un componente dinámico en una posición fija, y no un componente fijo en una posición dinámica.
También intenté seguir la publicación: Angular 2 Insertar dinámicamente un componente en un nodo específico del DOM sin usar ViewContainerRef
El Componente Sección es mi componente principal donde almaceno el HTML y Componente Valor es el componente que quiero reemplazar.
Componente de Sección:
HTML
<div [innerHTML]=sectionSnippet></div>
TS
@Component({
selector: 'section-view',
templateUrl: './section.component.html',
styleUrls: ['./section.component.sass'],
encapsulation: ViewEncapsulation.ShadowDom
})
export class SectionComponent implements OnInit {
@Input() sectionSnippetInput = '';
constructor(private sanitizer: DomSanitizer,
private resolver: ComponentFactoryResolver,
private injector: Injector,
private app: ApplicationRef) {
}
ngOnInit(): void {
this.replaceTags();
}
private replaceTags() {
let temp:HTMLDivElement = document.createElement('div');
temp.innerHTML = this.sectionSnippetInput;
let placeholders: HTMLCollectionOf<Element> = temp.getElementsByClassName('placeholderClass')
for (let i = 0; i < placeholders.length; i++) {
//dynamically create angular comp
let factory = this.resolver.resolveComponentFactory(ValueComponent);
//create instance with the value placeholder as parent
const ref = factory.create(this.injector, [], placeholders[i]);
//manually init comp
ref.instance.initElem = placeholders[i];
ref.instance.initComp();
//trigger re-rendering
ref.changeDetectorRef.detectChanges();
//attach newly created component to our angular context
this.app.attachView(ref.hostView);
}
this.sectionSnippetInput = temp.innerHTML;
}
get sectionSnippet() {
return this.sanitizer.bypassSecurityTrustHtml(this.sectionSnippetInput);
}
}
Componente de Valor:
HTML
<span (click)="testCallback()">{{getFormatNumber()}}</span>
TS
@Component({
selector: 'value',
templateUrl: './value.component.html',
styleUrls: ['./value.component.sass']
})
export class ValueComponent implements AfterViewInit {
public param1:string|null = '';
public initElem: Element | null | undefined;
public testCallback(){
alert("Test")
}
ngAfterViewInit(): void{
this.initComp();
}
public initComp() {
// @ts-ignore
this.param1 = this.initElem.getAttribute("data-placeholderdata");
}
getFormatNumber(){
//some formatting code
return this.param1;
}
}
Lo cual parece funcionar, al menos para la renderización inicial, pero los enlaces de Angular como (click) no funcionan. Mi suposición es que el componente recién creado no está correctamente vinculado al contexto de Angular, por lo tanto, no registra los eventos de clic.
(Usando Angular V13)
Como soy nuevo en Angular, ¿alguien puede darme consejos sobre cómo proceder? ¿Es una buena idea usar ComponentFactory o debería intentar algo diferente?