angular tutorial

Hace un tiempo escribí sobre cómo agregar componentes de forma dinamicas en angular, solo que la api que se usaba estaba apunto de estar en desuso.

para este momento esa forma ya no se usa pero explicare de forma breve cómo usarlo de otra forma.

Añadir componentes con angular

Las ventajas de angular es que los componentes tienen un comportamiento individual si por ejemplo tu creas un componente y en la misma pagina lo llamas 5 veces, cada llamada se comportara de forma individual.

con estas ventajas nos da oportunidad de usar componentes más dinámicos con angular.

Elegir Componentes dinámicos

Vamos a crear un nuevo proyecto para nuestros componentes dinámicos.

ng new componentesdinamicos

Una vez creado vamos a generar 5 componentes :

ng g c comp1
ng g c comp2
ng g c comp3
ng g c comp4
ng g c receptor

cada componente llevara lo siguiente (ejemplo)

<div>
    <h1>Hola</h1>
</div>

y en el css llevará el siguiente estilo, podemos cambiar el color para ver la diferencia de cada uno.

div {
    width: 300px;
    height: 300px;
    background-color: red;
    display: flex;
    align-content: center;
    align-items: center;
    color: white;
}

ahora vamos a crear una directiva para nuestro componente dinámico.

En Angular existen dos tipos de directivas:

  • Directivas de componente: Una directiva de componente es aquella que administra una región de HTML de una manera nativa como un elemento HTML. Técnicamente es una directiva con un template.
  • Directivas de atributo: Una directiva de atributo cambia la apariencia o comportamiento de un elemento, componente u otra directiva.

en otras palabras Angular nos provee de directivas estructurales para manipular el contenido de nuestras aplicaciones en el Document Object Model (DOM)

para mayor referencia les invito a leer el siguiente articulo

https://medium.com/angular-chile/plantillas-contenedores-y-directivas-estructurales-custom-en-angular-15d873ae9869

ng g d receptor

con esto tenemos lo siguiente (usaremos las siguientes referencias)

import { Directive, ViewContainerRef } from '@angular/core';

@Directive({
  selector: '[appReceptor]'
})
export class ReceptorDirective {

  constructor(public viewContainerRef: ViewContainerRef) { }

}

Ahora vamos a nuestro receptor.component

  • ViewChild – puedes elegir una vista en especifico
  • Input – enviar parametros entre componentes
  • ComponentFactoryResolver – componente que se encarga de insertar en la vista
  • viewContainerRef – Contenedor de la vista

Esto es lo que vamos usar en este código

import { Component, OnInit, ComponentFactoryResolver, ViewChild, Input } from '@angular/core';
import { Comp1Component } from '../comp1/comp1.component';
import { Comp2Component } from '../comp2/comp2.component';
import { Comp3Component } from '../comp3/comp3.component';
import { Comp4Component } from '../comp4/comp4.component';
import { ReceptorDirective } from '../receptor.directive';

@Component({
  selector: 'app-receptor',
  templateUrl: './receptor.component.html',
  styleUrls: ['./receptor.component.css']
})
export class ReceptorComponent implements OnInit {
  @ViewChild(ReceptorDirective, {static: true}) receptor: ReceptorDirective;
  @Input() compo: any;

  constructor(private componentFactoryResolver: ComponentFactoryResolver) { }

  ngOnInit() {
    let miComponent: any;
    switch (this.compo) {
      case '1':
        miComponent = Comp1Component;
        break;
      case '2':
        miComponent = Comp2Component;
        break;
      case '3':
        miComponent = Comp3Component;
        break;
      case '4':
        miComponent = Comp4Component;
        break;

    }

    if (miComponent) {
      let componentFactory = this.componentFactoryResolver.resolveComponentFactory(miComponent);
      let viewContainerRef = this.receptor.viewContainerRef;
      viewContainerRef.clear();
      viewContainerRef.createComponent(componentFactory);
    }
  }

}

Aquí creamos un switch donde diga que componente elegir.

viewContainerRef.clear() nos va a servir para limpiar el componente y poner otro en su lugar.

en el html necesitamos usar la etiqueta ng-template para que podamos insertar los componentes (TemplateRef de la directiva o usa)

<ng-template appReceptor></ng-template>

para que funcione todo muy bien en nuestros componentes dinámicos debemos de dar de alta en nuestro módulo los componentes creados anteriormente en entryComponents.

entryComponents Lo que hace es cargar componentes sin referencias a una plantilla, es decir al cargar los componentes en el ngmodule y lo podemos usar en cualquier parte sin hacer referencias.

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppComponent } from './app.component';
import { Comp1Component } from './comp1/comp1.component';
import { Comp2Component } from './comp2/comp2.component';
import { Comp3Component } from './comp3/comp3.component';
import { Comp4Component } from './comp4/comp4.component';
import { ReceptorComponent } from './receptor/receptor.component';
import { ReceptorDirective } from './receptor.directive';

@NgModule({
  declarations: [
    AppComponent,
    Comp1Component,
    Comp2Component,
    Comp3Component,
    Comp4Component,
    ReceptorComponent,
    ReceptorDirective
  ],
  imports: [
    BrowserModule
  ],
  providers: [],
  bootstrap: [AppComponent],
  entryComponents: [
    Comp1Component,
    Comp2Component,
    Comp3Component,
    Comp4Component
  ]
})
export class AppModule { }
añadir componentes dinámicos
componentes dinámicos elegidos

Con esto podemos ver la funcionalidad que tiene ComponentFactory, ahora la misma técnica podemos aplicar a un solo componente que seria el dinámico (componente 2).

Crear Componente Dinámico en angular

Tenemos que hacer el mismo procedimiento es decir:

  • Creamos una Directiva nueva
  • Creamos un componente para reutilizar
  • Programamos

Componente 2

<div [style.backgroundColor]="color">
    <h1> {{mensaje}} </h1>
</div>
div {
    width: 200px;
    height: 200px;
    background-color: black;
    display: flex;
    align-content: center;
    align-items: center;
    color: white;
    justify-content: center;
    margin: 5px;
    text-align: center;
    transition: all 2s cubic-bezier(0.175, 0.885, 0.32, 1.275);
    animation-duration: 500ms;
    animation-fill-mode: forwards;
    animation-name: zoom;
    transform: scale(0);
    transition-property: all;
    animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1.275);
}

@keyframes zoom {
    to {
        transform: scale(1);
    }
}

TypeScript

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

@Component({
  selector: 'app-comp2',
  templateUrl: './comp2.component.html',
  styleUrls: ['./comp2.component.css']
})
export class Comp2Component implements OnInit {
  modulonumero: number;
  constructor() { }
  mensaje: string;
  color: string

  ngOnInit() {

    if (sessionStorage.getItem('mimodulo')) {
      this.modulonumero = + sessionStorage.getItem('mimodulo');
      this.modulonumero ++;
      sessionStorage.setItem('mimodulo', this.modulonumero.toString());
    } else {
      this.modulonumero = 1;
      sessionStorage.setItem('mimodulo', this.modulonumero.toString());
    }
    if (sessionStorage.getItem('mensaje') === '') {
      this.mensaje = 'Hola soy el modulo numero: ' + this.modulonumero.toString();
    } else {
      this.mensaje = sessionStorage.getItem('mensaje');
    }
    if (sessionStorage.getItem('azar') === '1') {
      this.color = '#' + Math.floor(Math.random() * 16777215).toString(16);
    } else {
      this.color = sessionStorage.getItem('color');
    }
  }

}

Cambio en App.Component para agregar componentes dinámicos

Agregamos unos campos extras a app component (boton e inputs) y creamos una pequeña lógica donde guarda todo en session storage (si desean comunicar componente deberíamos usar servicios)

import { Component, OnInit, ComponentFactoryResolver, ViewChild, Input } from '@angular/core';
import {Comp2Component } from './comp2/comp2.component';
import {ColordinamicoDirective} from './colordinamico.directive';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
  @ViewChild(ColordinamicoDirective, {static: true}) eldinamico: ColordinamicoDirective;
  constructor(private cfr: ComponentFactoryResolver) {}
  cbox: boolean;
  ngOnInit() {
    this.cbox = true;
    sessionStorage.setItem('azar', '1');
  }
  cambio(e) {
    this.cbox = e;
    if (e) {
      sessionStorage.setItem('azar', '1');
    } else {
      sessionStorage.setItem('azar', '0');
    }
  }

  componenteDinamico(mensaje: string, color: string) {
    sessionStorage.setItem('mensaje', mensaje);
    sessionStorage.setItem('color', color);
    let cf = this.cfr.resolveComponentFactory(Comp2Component);
    let vcr = this.eldinamico.viewContainerRef;
    vcr.createComponent(cf, 0);
  }
}

CreateComponent puedes poner un index, si colocamos 0 este se colocara siempre al principio.

<h1>Prueba usando ComponentFactoryResolver</h1>

<h2>Componentes Hechos</h2>
<div class="orden">
    <app-receptor compo="1"></app-receptor>
    <app-receptor compo="3"></app-receptor>
    <app-receptor compo="4"></app-receptor>
</div>

<h2>Componente dinámico</h2>
<input type="text" #mensaje><br/>
<label><input type="checkbox" (change)="cambio($event.target.checked)" checked /> Color aleatorio</label><br/>
<input type="color" #color [disabled]="cbox"><br/>
<button (click)="componenteDinamico(mensaje.value, color.value)"> Crear Componente </button>
<br/>
<div class="orden">
    <ng-template appColordinamico></ng-template>
</div>

Con esto podemos tener componentes dinámicos, ya sea para mostrar o para crear.

Ejemplo en vivo

Repositorio

por Cesar Flores

Programador de tiempo completo, Gamer de medio tiempo y fotógrafo ocasionalmente, me gusta el front-end y mi framework favorito es angular aunque no por eso le hago el feo a un nuevo lenguaje.

Deja un comentario

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.