Como construir um componente para notificações - Angular
Como construir um componente para emitir notificações na tela do usuário

O objetivo deste artigo é mostrar como podemos construir um mecanismo para emitir notificações ao usuário.
Este mecanismo será composto por uma service e um componente. A ideia é que a service tenha a propriedade providedIn
com o valor root
em seu decorator para que haja uma única instância em toda a aplicação. Isso facilita bastante visto que a aplicação toda terá acesso a este única instancia que fará a ponte entre a aplicação toda e o componente de notificação.
Já o componente, que chamaremos de app-notificacoes
, será referenciado dentro do template do app.component.html
para que as notificações apareçam em todas as telas, visto que nosso <router-outlet>
está neste template (app-component.html
).
Construção da Service
Aqui não tem muito segredo, vamos estruturar a service da seguinte forma:
import { Injectable } from '@angular/core';
import { Observable, Subject } from 'rxjs';
import { Notificacao, NotificacaoAcao } from './notificacoes.component';
@Injectable({ providedIn: 'root' })
export class NotificacoesService {
notificacoes = new Subject<NotificacaoAcao>();
getNotificacoes(): Observable<NotificacaoAcao> {
return this.notificacoes.asObservable();
}
notificar(mensagem: string,
tempoDeVidaSegundos?: number) {
}
removerNotificacao(notificacao: Notificacao) {
}
removerNotificacoes() {
}
}
O esqueleto da service está logo acima mas antes de "rechearmos" os métodos, vamos comentar um pouco sobre o Subject<NotificacaoAcao>
.
O Subject
nos permite emitir valores, que no nosso caso será um objeto do tipo NotificacaoAcao
, e quem efetuar uma subscrição (subscribe) irá "ouvir/receber" estes valores. É mais ou menos quando você liga uma rádio no seu carro, a partir do momento que você sintonizou ("subscrição") a estação você passa a receber o audio.
A princípio quem irá "ouvir" estes valores emitidos será o app-notificacoes
que será construído mais a frente. Por enquanto vamos finalizar a construção da service e na sequência comentamos sobre os tipos Notificacao
e NotificacaoAcao
:
import { Injectable } from '@angular/core';
import { Observable, Subject, timer } from 'rxjs';
import { Notificacao, NotificacaoAcao } from './notificacoes.component';
@Injectable({ providedIn: 'root' })
export class NotificacoesService {
notificacoes = new Subject<NotificacaoAcao>();
getNotificacoes(): Observable<NotificacaoAcao> {
return this.notificacoes.asObservable();
}
notificar(mensagem: string,
tempoDeVidaSegundos?: number) {
const objNotificacao: Notificacao = {
mensagem,
tempoDeVidaSegundos,
};
this.notificacoes.next({
acao: 'novo',
notificacao: objNotificacao,
});
if (objNotificacao.tempoDeVidaSegundos) {
// Multiplicamos os segundos por 1000
// para obter milisegundos:
timer(objNotificacao.tempoDeVidaSegundos * 1000)
.subscribe(() => {
this.removerNotificacao(objNotificacao);
});
}
}
removerNotificacao(notificacao: Notificacao) {
this.notificacoes.next({
acao: 'remover',
notificacao
});
}
removerNotificacoes() {
this.notificacoes.next({
acao: 'remover-todas',
});
}
}
Veja nos métodos acima que estamos emitindo alguns valores através do this.notificacoes.next(...)
. A ideia é que o nosso componente, que será construído a seguir, receba estas informações e renderize na tela as mensagens de notificação.
Repare que há uma propriedade chamada acao
, que será interpretada pelo componente app-notificacoes
que veremos mais a frente.
O timer()
utilizado dentro do método notificar()
é como um setTimeout
, ele emite um valor no subscribe
sempre que o tempo passado como parâmetro é esgotado, por exemplo:
console.log('teste');
timer(3000).subscribe(() => {
console.log('Se passaram 3 segundos após o último console.log');
});
Utilizamos este recurso para as notificações que tem tempo de vida, ou seja, podemos emitir uma notificação já configurada para desaparecer após x segundos.
Construção do componente app-notificacoes
Primeiramente vamos criar dois type
, um para representar o objeto de notificação e outro para representar uma ação:
export type NotificacaoAcao = {
acao: 'novo'|'remover'|'remover-todas',
notificacao?: Notificacao,
};
export type Notificacao = {
mensagem: string,
tempoDeVidaSegundos?: number
}
Veja que teremos 3 possíveis cenários com o NotificacaoAcao
:
- incluir uma nova notificação:
{ acao: 'novo' }
- remover uma notificação específica:
{ acao: 'remover' }
- remover todas as notificações:
{ acao: 'remover-todas' }
Estas 3 opções serão interpretadas dentro do componente app-notificacoes
.
Agora focando no código do componente, temos que injetar a instância da service NotificacosService
no constructor
, efetuar um subscribe
no observable que emite as notificações da service e desenvolver a lógica para os três casos citados logo acima.
O código do componente ficará da seguinte forma:
import { Component } from '@angular/core';
import { Observable, Subscription, timer } from 'rxjs';
import { NotificacoesService } from './notificacoes.service';
export type NotificacaoAcao = {
acao: 'novo'|'remover'|'remover-todas',
notificacao?: Notificacao,
};
export type Notificacao = {
mensagem: string,
tempoDeVidaSegundos?: number
}
@Component({
selector: 'app-notificacoes',
styleUrls: ['./notificacoes.component.css'],
templateUrl: './notificacoes.component.html',
})
export class NotificacoesComponent {
notificacoes: Notificacao[] = [];
constructor(private notificacoesService: NotificacoesService) {
this.notificacoesService.getNotificacoes()
.subscribe((notificacaoAcao: NotificacaoAcao) => {
switch(notificacaoAcao.acao) {
case 'novo':
this.notificacoes.push(notificacaoAcao.notificacao);
break;
case 'remover':
this.notificacoes = this.notificacoes.filter(notificacao => {
return notificacao !== notificacaoAcao.notificacao;
});
break;
default:
this.notificacoes = [];
break;
}
});
}
remover(notificacao: Notificacao) {
this.notificacoesService
.removerNotificacao(notificacao);
}
}
Veja que temos uma variável notificacoes: Notificacoes[]
que utilizaremos como referência para renderizar as notificações no template.
O restante do código é apenas para gerenciar o conteúdo deste array a partir das mensagens que chegam no observable da service NotificacoesService
.
Código do template e na sequência do CSS:
<div>
<div *ngFor="let notificacao of notificacoes"
class="notificacao">
<div>{{notificacao.mensagem}}</div>
<div (click)="remover(notificacao)">x</div>
</div>
</div>
:host {
bottom: 0;
position: fixed;
right: 0;
top: 0;
width: 300px;
}
.notificacao {
background: #efefef;
border-radius: 8px;
color: #333;
display: flex;
margin: 8px;
padding: 24px;
}
.notificacao > div:nth-child(1) {
flex-grow: 1;
}
.notificacao > div:nth-child(2) {
cursor: pointer;
}
Testando as notificações
Agora com o componente pronto, vamos referenciá-lo no app.component.html
e adicionar alguns pontos de teste conforme o código abaixo:
<button (click)="notificar()">Notificar</button>
<button (click)="removerNotificacoes()">Limpar Notificações</button>
<app-notificacoes></app-notificacoes>
import { Component } from '@angular/core';
import { NotificacoesService } from './notificacoes/notificacoes.service';
@Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent {
constructor(private notificacoesService: NotificacoesService) {
}
notificar() {
this.notificacoesService.notificar(
`Teste: ${(new Date())}`,
this.numeroAleatorio(),
);
}
removerNotificacoes() {
this.notificacoesService.removerNotificacoes();
}
numeroAleatorio(min: number = 1, max: number = 5) {
return Math.floor(Math.random() * max) + min;
}
}
Resultado:

Quantidade de notificações
Para aproveitarmos este código e irmos um pouco além do objetivo deste artigo, vamos incrementar um pouco e mostrar no app.component.html
a quantidade de notificações na tela. Para isto vamos incluir um novo Subject<number>
e deixar o app-notificacoes
"avisar" a NotificacoesService
sobre a quantidade de visiveis notificações na tela:
import { Injectable } from '@angular/core';
import { Observable, Subject, timer } from 'rxjs';
import { Notificacao, NotificacaoAcao } from './notificacoes.component';
@Injectable({ providedIn: 'root' })
export class NotificacoesService {
notificacoesLength = new Subject<number>();
notificacoes = new Subject<NotificacaoAcao>();
getNotificacoes(): Observable<NotificacaoAcao> {
return this.notificacoes.asObservable();
}
// Obsersable que irá emitir os valores
// da quantidade de notificações visíveis na tela
getNotificacoesLength(): Observable<number> {
return this.notificacoesLength.asObservable();
}
// Este método será utilizado pelo app-notificacoes
// para "avisar" esta service sobre a nova quantidade
// de notificações
setNotificacoesLength(quantidade: number) {
this.notificacoesLength.next(quantidade);
}
notificar(mensagem: string,
tempoDeVidaSegundos?: number) {
const objNotificacao: Notificacao = {
mensagem,
tempoDeVidaSegundos,
};
this.notificacoes.next({
acao: 'novo',
notificacao: objNotificacao,
});
if (objNotificacao.tempoDeVidaSegundos) {
timer(objNotificacao.tempoDeVidaSegundos * 1000)
.subscribe(() => {
this.removerNotificacao(objNotificacao);
});
}
}
removerNotificacao(notificacao: Notificacao) {
this.notificacoes.next({
acao: 'remover',
notificacao
});
}
removerNotificacoes() {
this.notificacoes.next({
acao: 'remover-todas',
});
}
}
import { Component } from '@angular/core';
import { Subscription } from 'rxjs';
import { NotificacoesService } from './notificacoes.service';
export type NotificacaoAcao = {
acao: 'novo'|'remover'|'remover-todas',
notificacao?: Notificacao,
};
export type Notificacao = {
mensagem: string,
tempoDeVidaSegundos?: number
}
@Component({
selector: 'app-notificacoes',
styleUrls: ['./notificacoes.component.css'],
templateUrl: './notificacoes.component.html',
})
export class NotificacoesComponent {
notificacoes: Notificacao[] = [];
subscricao: Subscription;
constructor(private notificacoesService: NotificacoesService) {
this.subscricao = this.notificacoesService.getNotificacoes()
.subscribe((notificacaoAcao: NotificacaoAcao) => {
switch(notificacaoAcao.acao) {
case 'novo':
this.notificacoes.push(notificacaoAcao.notificacao);
break;
case 'remover':
this.notificacoes = this.notificacoes.filter(notificacao => {
return notificacao !== notificacaoAcao.notificacao;
});
break;
default:
this.notificacoes = [];
break;
}
// Alteração:
this.notificacoesService
.setNotificacoesLength(this.notificacoes.length);
});
}
remover(notificacao: Notificacao) {
this.notificacoesService
.removerNotificacao(notificacao);
}
}
Finalmente no AppComponent
:
import { Component } from '@angular/core';
import { Observable } from 'rxjs';
import { NotificacoesService } from './notificacoes/notificacoes.service';
@Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent {
notificacoesLength$: Observable<number>;
constructor(private notificacoesService: NotificacoesService) {
this.notificacoesLength$ = this.notificacoesService
.getNotificacoesLength();
}
notificar() {
this.notificacoesService.notificar(
`Teste: ${(new Date())}`,
this.numeroAleatorio(),
);
}
removerNotificacoes() {
this.notificacoesService.removerNotificacoes();
}
numeroAleatorio(min: number = 1, max: number = 5) {
return Math.floor(Math.random() * max) + min;
}
}
<h1>Notificações ({{(notificacoesLength$ | async) || '-'}})</h1>
<p>
Como construir um componente para notificações em Angular.
<a href="http://consolelog.com.br">consolelog.com.br</a>
</p>
<button (click)="notificar()">Notificar</button>
<button (click)="removerNotificacoes()">Limpar Notificações</button>
<app-notificacoes></app-notificacoes>
Veja que ao invés de efetuarmos um subscribe
explicíto no getNotificacoesLength()
, estamos utilizando um | async
para fazer este trabalho. O código fica bem mais limpo desta forma.
O resultado final:

Considerações
Este artigo apresenta uma forma bem legal de se compartilhar dados entre componentes com a utilização de services com o providedIn: 'root'
. Vale lembrar que existem outros métodos para isto e também para construir este esquema de notificação.
Podemos utilizar este recurso do Subject
para por exemplo "avisar" a aplicação toda que o usuário efetuou o login ou alterou algo em algum lugar que poderá refletir em outro.
Código completo e funcional:
