es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

Cómo renderizar componentes dinámicos en Angular 13 o superior.

Estoy trabajando en un proyecto de Angular 13 y necesito renderizar componentes dinámicos. La plantilla del componente y la lógica empresarial provienen del servidor como una cadena de texto. Leí que no se pueden crear plantillas dinámicamente en Angular 13, ya que cualquier componente debe ser compilado por Angular en tiempo de construcción, y las aplicaciones de Angular no se envían con el compilador JIT.

Esto es solo para una parte de la aplicación donde permito a los clientes crear sus propios paneles personalizados.

Era posible hacer esto en Angular 12, pero en Angular 13 el servicio del compilador ya no está expuesto.

Código funcionando en Angular 12:

Plantilla:

<div>
   <ng-container *ngcomponentoutlet="component"></ng-container>
</div>

Componente:

import { Compiler, Component, NgModule, OnInit } from '@angular/core';

@Component({
   selector: 'my-lab',
   templateUrl: './lab.component.html',
   styleUrls: ['./lab.component.scss'],
})
export class LabComponent implements OnInit {
   component: any;

   constructor(private compiler: Compiler) {}

   async ngOnInit() {
      const componentClass = class {
         public testing = {
            name: 'yago',
         };
      };
      const imports = (this.component = await this.compile({
         template: `<div>Probando aquí {{ testing | json}}</div>`,
         componentClass,
         imports: [CommonsModule],
      }));
   }

   async compile(input: any) {
      const { template, componentClass, imports } = input;
      const templateCompoment = Component({ template })(componentClass);

      const module = NgModule({
         declarations: [templateCompoment],
         exports: [templateCompoment],
         imports: [...imports],
      })(class {});

      const compiledModule = await this.compiler.compileModuleAndAllComponentsAsync(module);
      return templateCompoment;
   }
}

Me pregunto si es necesario utilizar un conjunto diferente de APIs en Angular 13 o superior para poder hacer esto. ¿Alguien lo ha hecho antes?

Tags:  , ,

Answer

  1. Avatar for davy.ai

    Como se mencionó en la pregunta, no es posible crear plantillas dinámicamente en Angular 13 utilizando el compilador JIT, lo que significa que el ejemplo de código proporcionado para Angular 12 no funcionará en la versión más reciente.

    Sin embargo, hay otras formas de lograr la representación dinámica de componentes en Angular 13. Uno de los enfoques es utilizar la API de resolución de fábricas de componentes de Ivy, que te permite crear y representar componentes dinámicamente en tiempo de ejecución.

    Aquí tienes un ejemplo de cómo puedes utilizar la API de resolución de fábricas de componentes para representar un componente dinámico:

    import { Component, ComponentFactoryResolver, NgModule, OnInit, Type, ViewChild, ViewContainerRef } from '@angular/core';
    
    @Component({
      selector: 'my-container',
      template: '<ng-template #target=""></ng-template>'
    })
    export class ContainerComponent implements OnInit {
      @ViewChild('target', { read: ViewContainerRef }) target: ViewContainerRef;
    
      constructor(private resolver: ComponentFactoryResolver) {}
    
      async ngOnInit() {
        // obtener datos del componente del servidor
        const componentData = await fetch('/api/component-data').then(res => res.json());
    
        // crear dinámicamente el componente y establecer entradas
        const componentFactory = this.resolver.resolveComponentFactory(componentData.componentClass);
        const componentRef = this.target.createComponent(componentFactory);
        componentRef.instance.setInputs(componentData.inputs);
      }
    }
    
    // uso en AppModule u otro módulo
    @NgModule({
      declarations: [ContainerComponent],
      imports: [CommonsModule],
      exports: [ContainerComponent],
      entryComponents: [/* lista de componentes creados dinámicamente */]
    })
    export class AppModule {}
    

    En este ejemplo, tenemos un componente de contenedor que tiene una referencia al contenedor de vista (target) donde representaremos dinámicamente nuestro componente. Utilizamos el ComponentFactoryResolver para crear una fábrica de componentes a partir de la clase del componente del lado del servidor y luego creamos el componente usando target.createComponent(). Luego podemos establecer cualquier entrada en la instancia del componente utilizando componentRef.instance.setInputs().

    Ten en cuenta que para que este enfoque funcione, necesitas agregar cualquier componente creado dinámicamente a la matriz entryComponents del módulo donde se declara el componente de contenedor.

    Existen otros enfoques para la representación dinámica de componentes en Angular 13, como utilizar la directiva ComponentOutlet o la API Compiler con compilación AOT, pero la API de resolución de fábricas de componentes es el enfoque recomendado por el equipo de Angular.

Comments are closed.