Ir para o conteúdo
Comunicação entre componentes usando @Input() em Angular

Comunicação entre componentes usando @Input() em Angular

Neste artigo, vamos nos concentrar em como um componente filho pode interagir com um componente pai usando a propriedade @Input(). Também examinaremos a interceptação da mensagem de entrada e o registro de alterações na mensagem de entrada.

8min read

No Angular, um componente pode compartilhar dados e informações com outro componente transmitindo dados ou eventos. Um componente pode ser usado dentro de outro componente, criando assim uma hierarquia de componentes. O componente que está sendo usado dentro de outro componente é conhecido como componente filho e o componente delimitador é conhecido como componente pai.

Parent Child component relation

Vamos considerar os componentes criados na lista abaixo. Criamos um componente chamado AppChildComponent, que será usado dentro de outro componente.

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

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

Também criamos outro componente chamado AppComponent. Dentro do 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";
}

Nas listagens acima, AppComonent está usando AppChildComponent, portanto, AppComponent é o componente pai e AppChildComponent é o componente filho.

Passando dados do componente pai para o componente filho

Vamos começar passando dados do componente pai para o componente filho. Isso pode ser feito usando a propriedade input. @Input decorador ou propriedades de entrada são usadas para passar dados do componente pai para o filho. Para fazer isso, precisaremos modificar o AppChildComponent filho, conforme mostrado na listagem abaixo:

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 você pode notar, modificamos a propriedade greetMessage com o decorador @Input(). Além disso, implementamos onInit, que será usado em demos posteriormente.  Então, essencialmente, no componente filho, decoramos a propriedade greetMessage com o decorador @Input() para que o valor da propriedade greetMessage possa ser definido a partir do componente pai.

Em seguida, vamos modificar o componente pai AppComponent para passar dados para o componente filho.

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"
}

No componente pai, estamos definindo o valor da propriedade greetMessage do componente filho. Para passar um valor para o componente filho, precisamos passar a propriedade do componente filho dentro de um colchete e definir seu valor para qualquer propriedade do componente pai. Estamos passando o valor da propriedade childmessage do componente pai para a propriedade greetMessage do componente filho.

passando o valor da propriedade childmessage do componente pai para a propriedade greetMessage

Interceptar entrada do componente pai no componente filho

Podemos ter um requisito para interceptar dados passados do componente pai dentro do componente filho. Isso pode ser feito usando getter e setter na propriedade input.

Digamos que desejamos interceptar uma mensagem recebida no componente filho e combiná-la com alguma string.  Para conseguir isso, criamos uma propriedade chamada _greetmessage e usando o decorador @Input() criando getter e setter para a propriedade _greetmessage. No getter, estamos interceptando a entrada do componente pai e combinando-a com a string. Isso pode ser feito conforme mostrado na próxima listagem:

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; }
}

No setter, estamos manipulando os dados recebidos do componente pai e anexando algum texto a ele. Lembre-se de que o comportamento do componente pai não mudaria se estivéssemos interceptando a mensagem ou não. Para explorá-lo ainda mais, vamos dar outro exemplo e criar um componente filho, que exibirá nomes passados pelo pai. Se o pai passar um valor de nome vazio, o componente filho exibirá algum nome padrão. Para fazer isso, modificamos o setter no componente filho. No setter, estamos verificando se o valor do nome é passado ou não. Se não for passado ou for uma string vazia, o valor de name será atribuído a um valor padrão. O componente filho pode ser criado conforme mostrado na listagem abaixo:

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 você pode observar no setter @Input(), estamos interceptando o valor passado do pai e verificando se ele é uma string vazia. Se for uma string vazia, estamos atribuindo o valor padrão para o nome no componente filho.

Podemos usar AppChildComponent dentro do componente pai AppComponent, conforme mostrado na listagem abaixo:

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 do componente pai, estamos fazendo um loop do componente filho por meio de todos os itens da propriedade childNameArray. Alguns itens do childNameArray são strings vazias, essas strings vazias seriam interceptadas pelo setter de componente filho e definidas como o valor padrão. O componente filho renderizará os valores passados do componente pai, conforme mostrado na imagem abaixo:

O componente filho renderizará os valores passados do componente pai

ngOnChanges e decorador @Input()

Podemos interceptar qualquer alteração de propriedade de entrada com o gancho do ciclo de vida OnChanges do componente. Pode haver vários motivos pelos quais a propriedade Input pode ser alterada no componente pai e precisa ser interceptada no componente filho. Isso pode ser feito no gancho do ciclo de vida OnChanges.

Vamos imaginar que precisamos registrar as alterações em uma variável de entrada chamada contador. O valor do contador é definido pelo componente pai e pode ser alterado no componente pai. Sempre que o valor do contador estiver mudando no componente pai, precisamos registrar as alterações no componente filho. Criamos um componente filho, conforme mostrado na listagem abaixo:

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(', '));
  }
}

Essencialmente, estamos fazendo o seguinte:

  1. Implementando o gancho do ciclo de vida OnChanges
  2. No método ngOnChanges, passando uma matriz do tipo SimpleChange
  3. Iterando por todas as alterações e enviando-as para uma matriz de cadeia de caracteres

Sempre que o valor de counter for alterado, o componente filho registrará o valor anterior e o valor atual. A leitura do valor anterior e do valor atual para cada propriedade de entrada é feita no método ngOnChnages conforme mostrado na imagem abaixo.

a leitura do valor anterior e do valor atual para cada propriedade de entrada é feita no método ngOnChnages conforme mostrado

No componente pai, estamos atribuindo um novo número aleatório para contar a propriedade ao clicar no botão e também definindo a propriedade count como o valor do contador de propriedades de entrada do componente filho.

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;
  }
}

Essencialmente, no componente pai, estamos definindo o valor do contador de propriedades de entrada do componente filho com a propriedade count do componente pai e também alterando seu valor a cada clique do botão.  Sempre que o valor da propriedade count for alterado no componente pai, o valor da propriedade de entrada do contador será alterado no componente filho e o valor alterado será registrado.

Ao executar o aplicativo, podemos obter a saída esperada com um log de alterações, conforme mostrado na imagem abaixo:

executando o aplicativo, podemos obter a saída esperada com um log de alterações

Conclusão

Neste artigo, aprendemos como passar dados de um componente pai para um componente filho. Também aprendemos como interceptar a mensagem de entrada e registrar as alterações. Em outras postagens, exploraremos mais aspectos da comunicação de componentes. Espero que você tenha achado este post útil, obrigado por ler.

Solicite uma demonstração