Saltar al contenido
Comunicación entre componentes mediante @Input() en Angular

Comunicación entre componentes mediante @Input() en Angular

En este artículo, nos centraremos en cómo un componente hijo puede interactuar con un componente padre utilizando la propiedad @Input(). También analizaremos la interceptación del mensaje de entrada y el registro de cambios en el mensaje de entrada.

8min read

En Angular, un componente puede compartir datos e información con otro componente pasando datos o eventos. Un componente se puede usar dentro de otro componente, creando así una jerarquía de componentes. El componente que se utiliza dentro de otro componente se conoce como componente secundario y el componente envolvente se conoce como componente principal.

Parent Child component relation

Consideremos los componentes creados en la lista a continuación. Hemos creado un componente llamado AppChildComponent, que se utilizará dentro de otro componente.

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

@Component({
  selector : 'appchild',
  template : `<h2> Hi{{greetMessage}} < / h2 >`
}) export class AppChildComponent {
  greetMessage : string = "I am Child";
}

También hemos creado otro componente llamado AppComponent. Dentro de AppComponent, estamos usando AppChildComponent:

import{Component} from '@angular/core';
import{AppChildComponent} from './appchild.component';

@Component({
  selector : 'my-app',
  template : `<h1> Hello{{message}} < / h1 > <br /><appchild></ appchild>
  `,
}) export class AppComponent {
  message : string = "I am Parent";
}

En los listados anteriores, AppComonent usa AppChildComponent, por lo tanto, AppComponent es el componente primario y AppChildComponent es el componente secundario.

Pasar datos del componente principal al componente secundario

Comencemos pasando datos del componente principal al componente secundario. Esto se puede hacer usando la propiedad input. @Input propiedades de decorador o entrada se utilizan para pasar datos del componente primario al secundario. Para ello, tendremos que modificar el AppChildComponent secundario como se muestra en la siguiente lista:

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

@Component({
  selector : 'appchild',
  template : `<h2> Hi{{greetMessage}} < / h2 >`
}) 

export class AppChildComponent implements OnInit {
  @Input() greetMessage : string;
  constructor() {}
  ngOnInit() {}
}

Como puede ver, hemos modificado la propiedad greetMessage con el decorador @Input(). Además, hemos implementado onInit, que se utilizará en demostraciones más adelante.  Entonces, esencialmente, en el componente secundario, hemos decorado la propiedad greetMessage con el decorador @Input() para que el valor de la propiedad greetMessage se pueda establecer desde el componente principal.

A continuación, modifiquemos el componente principal AppComponent para pasar datos al componente secundario.

import{Component} from '@angular/core';
import{AppChildComponent} from './appchild.component';

@Component({
  selector : 'my-app',
  template : `<h1> Hello{{message}} < / h1 > <br />
      <appchild[greetMessage] = "childmessage"></ appchild>
  `,
}) 
export class AppComponent {
  message : string = "I am Parent";
  childmessage : string = "I am passed from Parent to child component"
}

Desde el componente primario, establecemos el valor de la propiedad greetMessage del componente secundario. Para pasar un valor al componente secundario, debemos pasar la propiedad del componente secundario dentro de un corchete y establecer su valor en cualquier propiedad del componente principal. Estamos pasando el valor de la propiedad childmessage del componente primario a la propiedad greetMessage del componente secundario.

pasar el valor de la propiedad childmessage del componente primario a la propiedad greetMessage

Interceptar la entrada del componente principal en el componente secundario

Es posible que tengamos el requisito de interceptar los datos pasados desde el componente principal dentro del componente secundario. Esto se puede hacer usando getter y setter en la propiedad de entrada.

Digamos que deseamos interceptar un mensaje entrante en el componente secundario y combinarlo con alguna cadena.  Para lograr esto, creamos una propiedad llamada _greetmessage y usamos el decorador @Input() creando getter y setter para la propiedad _greetmessage. En el captador, interceptamos la entrada del componente principal y la combinamos con la cadena. Esto se puede hacer como se muestra en el siguiente listado:

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

@Component({
  selector : 'appchild',
  template : `<h2> Hi{{_greetMessage}} < / h2 >`
}) 

export class AppChildComponent implements OnInit {
  _greetMessage : string;
  constructor() {}
  ngOnInit() {}

  @Input() set greetMessage(message : string) {
    this._greetMessage = message + " manipulated at child component";
  }
  get greetmessage() { return this._greetMessage; }
}

En el establecedor, estamos manipulando los datos entrantes del componente principal y agregando texto a eso. Tenga en cuenta que el comportamiento del componente principal no cambiaría si estamos interceptando el mensaje o no. Para explorarlo más a fondo, tomemos otro ejemplo y creemos un componente secundario, que mostrará los nombres pasados desde el padre. Si el elemento principal pasa un valor de nombre vacío, el componente secundario mostrará un nombre predeterminado. Para ello, hemos modificado el establecedor en el componente hijo. En el establecedor, estamos comprobando si el valor del nombre se pasa o no. Si no se pasa o es una cadena vacía, el valor de name se asignaría a un valor predeterminado. El componente secundario se puede crear como se muestra en la lista a continuación:

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

@Component({
  selector : 'appchild',
  template : `<h2>{{_name}} < / h2 >`
}) 

export class AppChildComponent implements OnInit {
  _name : string;
  constructor() {}
  ngOnInit() {}

  @Input() set Name(name : string) {
    this._name = (name && name.trim()) || "I am default name";
  }
  get Name() { return this._name; }
}

Como observa en el establecedor @Input(), estamos interceptando el valor pasado desde el padre y verificando si es una cadena vacía. Si es una cadena vacía, estamos asignando el valor predeterminado para el nombre en el componente secundario.

Podemos usar AppChildComponent dentro del componente principal AppComponent como se muestra en la lista a continuación:

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

@Component({
  selector : 'my-app',
  template : `<h1> Hello{{message}} < / h1 > <br />

      <appchild *ngFor = "let n of childNameArray"[Name] = "n"></ appchild>
  `,
}) 
export class AppComponent {
  message : string = "I am Parent";
  childmessage
      : string = "I am passed from Parent to child component" childNameArray =
            [ 'foo', 'koo', ' ', 'moo', 'too', 'hoo',''];
}

Dentro del componente principal, estamos recorriendo el componente secundario a través de todos los elementos de la propiedad childNameArray. Algunos elementos de childNameArray son cadenas vacías, estas cadenas vacías serían interceptadas por el establecedor de componentes secundarios y se establecerían en el valor predeterminado. El componente secundario renderizará los valores pasados desde el componente principal como se muestra en la imagen siguiente:

El componente secundario renderizará los valores pasados desde el componente principal

ngOnChanges y el decorador @Input()

Podemos interceptar cualquier cambio en la propiedad de entrada con el enlace de ciclo de vida OnChanges del componente. Puede haber varias razones por las que la propiedad Input se puede cambiar en el componente primario y debe interceptarse en el componente secundario. Esto se puede hacer en el enlace de ciclo de vida OnChanges.

Imaginemos que necesitamos registrar los cambios en una variable de entrada llamada counter. El valor del contador lo establece el componente principal y se puede cambiar en el componente principal. Siempre que el valor del contador cambie en el componente principal, debemos registrar los cambios en el componente secundario. Creamos un componente secundario como se muestra en la lista a continuación:

import{Component, Input, OnChanges, SimpleChange} from '@angular/core';

@Component({
  selector : 'appchild',
  template : `<h2>{{counter}} < / h2 > <h2> Value Changed</ h2><ul>
      <li *ngFor = "let c of changeLog">{{c}} < / li >
      </ ul> 
    `
}) 

export class AppChildComponent implements OnChanges {
  @Input() counter = 0;
  changeLog : string[] = [];
  constructor() {}

  ngOnChanges(changes : {[propKey:string] : SimpleChange}) {
    let log : string[] = [];
    for (let p in changes) {
      let c = changes[p];
      console.log(c);
      let from = JSON.stringify(c.previousValue);
      let to = JSON.stringify(c.currentValue);
      log.push(`${p} changed from ${from} to $ { to }`);
    }

    this.changeLog.push(log.join(', '));
  }
}

Esencialmente estamos haciendo lo siguiente:

  1. Implementación del enlace de ciclo de vida OnChanges
  2. En el método ngOnChanges, pasar una matriz de tipo SimpleChange
  3. Iterar a través de todos los cambios y enviarlos a una matriz de cadenas

Siempre que se cambie el valor de counter, el componente secundario registrará el valor anterior y el valor actual. La lectura del valor anterior y el valor actual de cada propiedad de entrada se realiza en el método ngOnChnages, como se muestra en la imagen siguiente.

la lectura del valor anterior y el valor actual de cada propiedad de entrada se realiza en el método ngOnChnages como se muestra

En el componente primario, estamos asignando un nuevo número aleatorio para contar la propiedad al hacer clic en el botón, y también estableciendo la propiedad count como el valor del contador de propiedades de entrada del componente secundario.

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

@Component({
  selector : 'my-app',
  template : `<h1> Hello{{message}} < / h1 >
      <br /><button(click) = "nextCount()"> change count</ button><br />
      <appchild[counter] = "count"></ appchild> 
  `,
}) 

export class AppComponent {
  count : number = 0;

  nextCount() {
    console.log("hahahah");
    this.count = this.getRandomIntInclusive(this.count, this.count + 1000);
  }

  getRandomIntInclusive(min : number, max : number) {
    min = Math.ceil(min);
    max = Math.floor(max);
    return Math.floor(Math.random() * (max - min + 1)) + min;
  }
}

Básicamente, en el componente principal, establecemos el valor del contador de propiedades de entrada del componente secundario con la propiedad count del componente principal y también cambiamos su valor con cada clic del botón.  Siempre que el valor de la propiedad count cambie en el componente primario, el valor de la propiedad de entrada del contador se cambiará en el componente secundario y se registrará el valor modificado.

Al ejecutar la aplicación, podemos obtener el resultado esperado con un registro de cambios como se muestra en la imagen siguiente:

al ejecutar la aplicación, es posible que obtengamos el resultado esperado con un registro de cambios

Conclusión

En este artículo, aprendimos cómo pasar datos de un componente principal a un componente secundario. También aprendimos a interceptar el mensaje de entrada y registrar los cambios. En publicaciones posteriores, exploraremos más aspectos de la comunicación de componentes. Espero que hayas encontrado útil esta publicación, gracias por leer.

Solicitar una demostración