Executando verificação de segurança...
4

Comunicação entre Componentes no Angular: 3 Formas + Patterns

Em aplicações Angular, frequentemente precisamos que componentes troquem informações de modo eficiente. Vamos ver três formas principais de resolver isso:

  1. Pai para Filho: com @Input().
  2. Filho para Pai: com @Output() e EventEmitter.
  3. Qualquer Componente para Qualquer Componente: via Service (Serviço) com Observable (ou Subject).

Esses mecanismos se apoiam em padrões de projeto como Observer, Publish–Subscribe (Pub–Sub) e Mediator:

  • Observer: Um objeto (Subject) mantém uma lista de observadores (Observers) e notifica todos quando ocorre uma mudança.
  • Pub–Sub (variação do Observer): Em vez de “observar” diretamente, há um “canal” no qual Publishers publicam e Subscribers se inscrevem. Todos os inscritos no canal recebem as publicações.
  • Mediator: Um mediador central coordena a comunicação entre objetos, evitando que eles se refiram diretamente uns aos outros. Um serviço Angular pode funcionar como esse “mediador”.

Para aprofundar mais nesses padrões, confira estes artigos:


1. Pai → Filho com @Input()

Como funciona?

  • No componente Filho, criamos uma propriedade marcada com @Input().
  • No Pai, usamos property binding [propriedade]="valor" para passar dados.

Exemplo

child.component.ts:

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

@Component({
  selector: 'app-child',
  template: `
    <h2>{{ titulo }}</h2>
  `
})
export class ChildComponent {
  @Input() titulo!: string;
}

parent.component.ts:

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

@Component({
  selector: 'app-parent',
  template: `
    <app-child [titulo]="tituloPai"></app-child>
  `
})
export class ParentComponent {
  tituloPai = 'Título vindo do Pai';
}

Relacionamento com o Observer Pattern

Embora simples, a ideia de “atualizar o valor recebido” e refletir no template remete ao fato de que o filho “observa” o valor — mas aqui é o Angular que faz boa parte do trabalho de binding. É um fluxo unidirecional: Pai → Filho.


2. Filho → Pai com @Output() + EventEmitter

Como funciona?

  • No componente Filho, criamos um @Output() que expõe um EventEmitter<T>.
  • O Pai faz event binding usando (), capturando o valor emitido.

Exemplo

child.component.ts:

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

@Component({
  selector: 'app-child',
  template: `
    <h2>{{ titulo }}</h2>
    <button (click)="enviarMensagem()">Enviar mensagem para o Pai</button>
  `
})
export class ChildComponent {
  @Input() titulo!: string;
  @Output() mensagemParaPai = new EventEmitter<string>();

  enviarMensagem() {
    this.mensagemParaPai.emit('Olá, componente Pai! Mensagem do Filho.');
  }
}

parent.component.ts:

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

@Component({
  selector: 'app-parent',
  template: `
    <app-child
      [titulo]="tituloPai"
      (mensagemParaPai)="receberMensagem($event)">
    </app-child>

    <p>Mensagem recebida do Filho: {{ mensagemRecebida }}</p>
  `
})
export class ParentComponent {
  tituloPai = 'Título do Pai';
  mensagemRecebida = '';

  receberMensagem(msg: string) {
    this.mensagemRecebida = msg;
  }
}

Relacionamento com Observer e Pub–Sub

O EventEmitter utiliza internamente um Subject do RxJS, que segue o Observer Pattern. O componente pai “assina” (no template) o evento. O filho “publica” (emite) quando algo ocorre. Isso pode ser visto também como um pequeno Pub–Sub, mas limitado ao escopo Pai-Filho.


3. Entre componentes sem relação direta: Service + Observable / Subject

Quando não existe relação pai-filho ou precisamos que vários componentes troquem dados, podemos usar um Service como ponto central (um “mediador”). Esse service terá um Subject ou BehaviorSubject (do RxJS) para emitir e observar eventos/dados.

Exemplo simples (Broadcast Global)

communication.service.ts

import { Injectable } from '@angular/core';
import { Subject, Observable } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class CommunicationService {
  private mensagemSubject = new Subject<string>();

  enviarMensagem(msg: string): void {
    this.mensagemSubject.next(msg);
  }

  observarMensagens(): Observable<string> {
    return this.mensagemSubject.asObservable();
  }
}
  • Subject<string> permite chamar subject.next(...) para emitir um valor do tipo string.
  • observarMensagens() retorna um Observable<string> para que os componentes possam se inscrever (subscribe()).

Componente A (envia)

import { Component } from '@angular/core';
import { CommunicationService } from '../services/communication.service';

@Component({
  selector: 'app-componente-a',
  template: `
    <button (click)="enviar()">Enviar Mensagem</button>
  `
})
export class ComponenteA {
  constructor(private communicationService: CommunicationService) {}

  enviar() {
    this.communicationService.enviarMensagem('Olá do Componente A!');
  }
}

Componente B (recebe)

import { Component, OnInit, OnDestroy } from '@angular/core';
import { Subscription } from 'rxjs';
import { CommunicationService } from '../services/communication.service';

@Component({
  selector: 'app-componente-b',
  template: `
    <p>Mensagem Recebida: {{ mensagem }}</p>
  `
})
export class ComponenteB implements OnInit, OnDestroy {
  mensagem = '';
  private subscription!: Subscription;

  constructor(private communicationService: CommunicationService) {}

  ngOnInit(): void {
    this.subscription = this.communicationService.observarMensagens()
      .subscribe((msg) => {
        this.mensagem = msg;
      });
  }

  ngOnDestroy(): void {
    this.subscription.unsubscribe();
  }
}

Observações

  • Todos os componentes que chamarem communicationService.observarMensagens() e fizerem subscribe() receberão todas as mensagens emitidas.
  • Se esse não for o comportamento desejado, você pode:
    • Criar múltiplos Subjects para diferentes tipos de evento.
    • Passar um objeto com { tipo, payload } e filtrar nos componentes (Pub–Sub mais elaborado).
    • Usar State Management (NgRx, Akita, etc.) em casos maiores.

Padrões: Pub–Sub e Mediator

  • O Subject funciona como um Publisher e quem chama subscribe() é o Subscriber.
  • O Service atua como um mediador (ver Mediator Pattern): ele permite que componentes não se conheçam diretamente, mas se comuniquem via um canal comum.

Para mais detalhes sobre Pub–Sub, veja o artigo “Pub-Sub Pattern: conceito e exemplo”. E para se aprofundar na ideia de ter um “objeto mediador” que centraliza interações, veja Mediator Pattern na Refactoring Guru.


Conclusão

No Angular, a comunicação entre componentes se divide em:

  1. Pai → Filho: @Input().
  2. Filho → Pai: @Output() + EventEmitter.
  3. Sem relação de parentesco: serviço com Observable/Subject (Pub–Sub).

Cada abordagem corresponde a padrões de projeto clássicos:

  • Observer (quando um componente “emite” e outro “escuta”, como EventEmitter).
  • Publish–Subscribe (Pub–Sub) (vários inscritos recebendo mensagens).
  • Mediator (o serviço angular faz o papel de mediador entre partes que não se conhecem diretamente).

Para entender melhor cada padrão, confira:

Dica: Em aplicações grandes, você pode adotar state management (NgRx, Akita, etc.), que segue princípios parecidos, porém organizados em fluxos de ação, redução de estado e centralização do dado global. Mas, para a maioria dos casos médios ou pequenos, as três formas acima são mais que suficientes para lidar com a comunicação de componentes no Angular.

Boas codificações!

Carregando publicação patrocinada...