Experts in Angular

Guias DetalhadoA Arte de Transmitir Mensagens no Angular
Emissão de evento do filho para o pai usando @Output()

A Arte de Transmitir Mensagens no Angular

Um Relato Sobre Comunicação Pai-Filho, Irmãos e Além

Imagine que você está em uma sala de conferência, diante de um grande quadro branco, e nela há uma linha central que liga dois pontos: um é o componente “Pai”, outro é o componente “Filho”.
A forma como esses dois pontos trocam sinais sempre foi um dos pilares mais importantes do Angular, garantindo a coesão de toda a aplicação.

E, em Angular, quando falamos em comunicação entre componentes, estamos falando justamente dessa interação que dá vida a interfaces poderosas.

Neste artigo, vamos explorar quatro grandes abordagens para emissão de eventos e passagem de dados entre componentes no Angular:

(1) do filho para o pai com @Output(),

(2) do pai para o filho com @Input(),

(3) comunicação entre irmãos via Service + RxJS, e

(4) o (menos comum, mas às vezes inevitável) uso de EventEmitter entre componentes sem parentesco direto.

1. Emissão de evento do filho para o pai usando @Output()

Em uma metáfora familiar, podemos pensar no “filho” levantando a mão para chamar a atenção do “pai” e dizer “Ei, aconteceu algo importante aqui!”.
Em Angular, o @Output() é essa mão levantada.

Exemplo de implementação

Componente Filho (filho.component.ts):

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

@Component({
  selector: 'app-filho',
  template: `
    <button (click)="notificarPai()">Notificar Pai</button>
  `
})
export class FilhoComponent {
  @Output() notifiquePai = new EventEmitter<string>();

  notificarPai() {
    const mensagem = 'Olá, Pai! Aqui é o Filho.';
    this.notifiquePai.emit(mensagem);
  }
}

Componente Pai (pai.component.html):

<app-filho (notifiquePai)="tratarNotificacao($event)"></app-filho>

Componente Pai (pai.component.ts):

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

@Component({
  selector: 'app-pai',
  templateUrl: './pai.component.html'
})
export class PaiComponent {
  tratarNotificacao(mensagem: string) {
    console.log('Recebi do Filho:', mensagem);
  }
}

Quando o filho chama this.notifiquePai.emit(...), o pai é imediatamente avisado, podendo reagir da maneira que julgar apropriada — seja registrando um log, seja atualizando uma propriedade, ou até mesmo exibindo um modal.
Essa comunicação mantém as responsabilidades claras e ajuda a manter a lógica de cada parte bem organizada.

Breves Explicações sobre Conceitos-Chave

@Output()
Decorador que transforma uma propriedade em um EventEmitter, permitindo que o componente filho envie eventos para o pai.

EventEmitter
Classe que emite eventos personalizados no Angular. Permite que um componente “dispare” valores ou objetos para quem estiver ouvindo através da sintaxe (eventoNome)="...".

2. Passar dados do pai para o filho usando @Input()

Se no caso anterior tínhamos o filho “levantando a mão”, agora é o pai quem “instrui” o filho.
Essa é a via natural de dados quando precisamos inicializar ou alterar valores dentro de um componente filho, baseado em algum estado ou cálculo feito no pai.

Exemplo de implementação

Componente Filho (filho.component.ts):

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

@Component({
  selector: 'app-filho',
  template: `
    <div>
      Mensagem do Pai: {{ mensagemDoPai }}
    </div>
  `
})
export class FilhoComponent {
  @Input() mensagemDoPai: string;
}

Componente Pai (pai.component.html):

<app-filho [mensagemDoPai]="'Olá do Pai!'"></app-filho>

Repare que aqui o pai não “espera” feedback.
Ele simplesmente coloca o dado no componente filho por meio da propriedade [mensagemDoPai].
Assim, o filho não precisa ficar “perguntando” ou “esperando” — ele já recebe a informação de bandeja e pode usá-la diretamente no seu template.

Breves Explicações sobre Conceitos-Chave

@Input()
Decorador que permite receber dados de um componente pai.

3. Comunicação entre “irmãos” (ou componentes distantes) via Service + RxJS

Se no universo de Angular a comunicação pai-filho é instintiva, o que dizer de irmãos ou componentes que estão distantes na hierarquia, separados por diversos outros componentes ou módulos?

Essa é uma das situações em que um “mensageiro” se faz necessário — e esse mensageiro toma a forma de um Service em conjunto com RxJS.

Pense nisso como um jornal em tempo real que qualquer componente pode publicar e assinar. Quando alguém publica uma notícia, todos os assinantes recebem ao mesmo tempo.

Exemplo de implementação

Service (comunicacao.service.ts):

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

@Injectable({
  providedIn: 'root'
})
export class ComunicacaoService {
  private _notificador = new Subject<string>();
  notificador$ = this._notificador.asObservable();

  emitirMensagem(msg: string) {
    this._notificador.next(msg);
  }
}

Componente A (emite dados) (componente-a.component.ts):

import { Component } from '@angular/core';
import { ComunicacaoService } from './comunicacao.service';

@Component({
  selector: 'app-componente-a',
  template: `<button (click)="enviar()">Enviar para irmãos</button>`
})
export class ComponenteA {
  constructor(private comunicacaoService: ComunicacaoService) {}

  enviar() {
    this.comunicacaoService.emitirMensagem('Olá de A!');
  }
}

Componente B (recebe dados) (componente-b.component.ts):

import { Component, OnInit, OnDestroy } from '@angular/core';
import { ComunicacaoService } from './comunicacao.service';
import { Subscription } from 'rxjs';

@Component({
  selector: 'app-componente-b',
  template: `<div>Recebi: {{ mensagem }}</div>`
})
export class ComponenteB implements OnInit, OnDestroy {
  mensagem: string;
  private subscription: Subscription;

  constructor(private comunicacaoService: ComunicacaoService) {}

  ngOnInit() {
    this.subscription = this.comunicacaoService.notificador$
      .subscribe((msg) => {
        this.mensagem = msg;
        console.log('Componente B recebeu:', msg);
      });
  }

  ngOnDestroy() {
    if (this.subscription) {
      this.subscription.unsubscribe();
    }
  }
}

Agora, quando o Componente A chama emitirMensagem('Olá de A!'), o Componente B e todos que estiverem inscritos em notificador$ recebem a mensagem.

Esse padrão permite que qualquer componente que precise “ouvir” um evento de outro local na aplicação possa fazê-lo sem depender de vínculos de hierarquia ou de passagens de @Input() e @Output() entre vários níveis.

Breves Explicações sobre Conceitos-Chave

@Injectable
Esse decorador informa ao Angular que a classe pode participar do sistema de injeção de dependências (Dependency Injection).

Significa que podemos instanciar a classe e “injetá-la” em componentes ou outros serviços, tornando o código mais modular e testável.


providedIn: 'root'
É uma forma de indicar que o serviço (service) estará disponível para toda a aplicação, sem precisar declará-lo em um módulo específico (como no providers de um @NgModule).

Ao usar providedIn: 'root', o Angular garante que haja apenas uma instância desse serviço em toda a aplicação.


asObservable()
Esse método é usado para expor um Subject como um Observable. Em outras palavras, você oculta a capacidade de “emitir” valores e somente permite que outras partes do código “observem” (ou “escutem”) as mudanças.

Isso mantém o encapsulamento, evitando que componentes externos enviem dados diretamente.


next(msg);
É o método responsável por “enviar” ou “propagar” um valor para todos os inscritos em um Subject ou BehaviorSubject

Quando chamamos next(msg), todos os assinantes (subscribers) recebem a mensagem imediatamente, possibilitando a atualização reativa do estado ou da interface.


constructor(private comunicacaoService: ComunicacaoService);
Dentro de um componente, quando declaramos um parâmetro no construtor com private (ou public) e atribuímos a ele um serviço (por exemplo, ComunicacaoService), estamos usando a injeção de dependência do Angular.

O framework cria (ou recupera) a instância do serviço e o disponibiliza no componente para uso interno.


.subscribe
É o método responsável por “assinar” um Observable. Quando chamamos .subscribe, informamos o que deve ser feito com cada valor que o Observable emitir (via next(msg);)

Sem a chamada de subscribe, não há “consumo” dos eventos, e nenhuma ação é disparada.


ngOnDestroy()
Esse é um ciclo de vida de um componente em Angular que é chamado pelo framework antes que o componente seja destruído ou removido da tela.

É o local ideal para “limpar” recursos, cancelar assinaturas de observables (unsubscribe) e evitar vazamentos de memória.


.unsubscribe()
Permite que você encerre a assinatura de um Observable. Isso impede que o seu componente continue recebendo notificações depois que ele já não está mais em uso.

Evita problemas de desempenho e vazamentos de memória, pois o Observable deixa de emitir valores para quem não precisa mais escutar.

4. EventEmitter entre componentes sem parentesco direto (menos comum)

Como último ponto, vale mencionar que, embora seja tecnicamente possível usar @Output() para conectar componentes que não sejam pai-filho, o processo torna-se extremamente complicado com o aumento de complexidade.

Cada componente intermediário precisa “encaminhar” o evento, criando um emaranhado que não é apenas difícil de manter, mas que viola a ideia de simplificar responsabilidades.

Por isso, a forma mais comum e organizada de lidar com esse cenário é mesmo o Service + RxJS.

Só use EventEmitter diretamente entre componentes distantes se tiver um cenário muito específico e controlado.

Seja através de uma simples emissão de evento do filho para o pai, ou de uma elaborada rede de “irmãos” que compartilham mensagens via Service + RxJS, o Angular oferece diversas maneiras de gerenciar a comunicação entre componentes. Cada estratégia tem sua razão de ser — como um maestro que escolhe o instrumento certo para cada parte da sinfonia, o desenvolvedor Angular precisa conhecer bem esses recursos para criar aplicações de forma elegante, coerente e, acima de tudo, organizada.

Em Angular, a troca de eventos é que impulsiona a interatividade e a harmonia entre os pedaços da aplicação. Com essas técnicas, você estará não só dominando a arte de comunicar dados, mas também criando bases sólidas para um código mais limpo e manutenível.

Sinta-se livre para adaptar e refinar esse roteiro de comunicação ao seu contexto. Afinal, a melhor arquitetura é aquela que equilibra perfeitamente simplicidade e poder.