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:
- Pai para Filho: com
@Input()
. - Filho para Pai: com
@Output()
eEventEmitter
. - Qualquer Componente para Qualquer Componente: via Service (Serviço) com
Observable
(ouSubject
).
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:
- Observer Pattern: Refactoring Guru
- Pub–Sub Pattern (conceito e exemplo): dev.to/jucian0
- Mediator Pattern: Refactoring Guru
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 umEventEmitter<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 chamarsubject.next(...)
para emitir um valor do tipostring
.observarMensagens()
retorna umObservable<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 fizeremsubscribe()
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:
- Pai → Filho:
@Input()
. - Filho → Pai:
@Output()
+EventEmitter
. - 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:
- Observer Pattern — Refactoring Guru
- Pub–Sub Pattern (artigo dev.to/jucian0)
- Mediator Pattern — Refactoring Guru
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!