Lidando com Elementos Globais em Aplicações Sever-Side Rendering (SSR) no Angular

Gabriel de Souza
3 min readOct 7, 2024

--

Você já tentou adicionar eventos em elementos globais, window e document por exemplo, em uma aplicação de Server-Side Rendering (SSR)? Se sim, você provavelmente se deparou com este erro:

Erro no elemento global ‘window’ em aplicações SSR

Geralmente esse erro ocorre quando tentamos manipular elementos globais como o window, porém, em aplicações SSR esses objetos não existem até que seja renderizado no client-side. Para isso, o Angular fornece uma forma simples de lidar com isso, a classe Renderer2.

Conhecendo o Renderer2

O Renderer2 é uma classe abstrata que pode ser extendida para implementar renderização customizada de elementos. O que vamos usar é o método ‘listen’ injetando o Renderer2 no componente:

protected readonly renderer = inject(Renderer2);

Exemplo: Criando um Componente

Neste exemplo, nós vamos criar um componente para deslizar a tela automaticamente para o topo da página, com esse simples evento nós usamos o elemento global ‘window’ e ainda criamos um componente legal de para algumas aplicações.

Vamos começar gerando o componente com o CLI do Angular:

ng generate component scroll-to-top

Arquivo: scroll-to-top.component.ts

import {
AfterContentInit,
Component,
ElementRef,
inject,
OnDestroy,
Renderer2,
signal,
viewChild,
} from '@angular/core';

@Component({
...
selector: 'scroll-to-top',
templateUrl: './scroll-to-top.component.html',
styleUrl: './scroll-to-top.component.scss',
...
})
export class ScrollToTopComponent implements AfterContentInit, OnDestroy {
protected readonly scrollButton = viewChild.required('scrollButton', {
read: ElementRef,
});

protected readonly isDisabled = signal<boolean>(true);
protected readonly renderer = inject(Renderer2);
protected unListen: () => void;

constructor() {
this.unListen = this.renderer.listen('window', 'scroll', () => {
if (scrollY > 300) {
this.isDisabled.set(false);
} else {
this.isDisabled.set(true);
}
});
}

ngAfterContentInit(): void {
const scroll = this.scrollButton().nativeElement;
this.unListen = this.renderer.listen(scroll, 'click', () => {
window.scrollTo({ top: 0, behavior: 'smooth' });
});
}

ngOnDestroy(): void {
this.unListen();
}
}

No código acima, nós criamos um evento no elemento ‘window’ (entre aspas para o Angular saber como tratar o elemento) no constructor do componente, se a posição do scroll na tela for maior que 300 nós habilitamos o clique nele. Usamos o hook de ‘afterContentInit’ para criar o evento de deslizar para o topo da tela (scroll to top). Em ambos os eventos criados nós atribuimos ele a uma função ‘unListen’ do tipo void, isso é essencial para remover o evento quando destruimos o componente.

Sem o Renderer2, você teria que implementar a verificação da plataforma que o usuário está com o ‘isPlatformBrowser’. Com o método ‘listen’ do Renderer2 os eventos são criados apenas no client-side, e não no server-side, evitando erros com elementos globais.

Arquivo: scroll-to-top.component.html

<button #scrollButton [disabled]="this.isDisabled()">↑</button>

No template, colocamos apenas um simples botão.

Arquivo: scroll-to-top.component.scss

button {
position: fixed;
bottom: 4svh;
right: 4svw;
}

O estilo fica a seu critério, no exemplo acima, apenas fixei a posição do botão na tela.

Informações Adicionais

Quero deixar claro que não sou especialista no assunto. Se cometi algum erro ou passei informações incorretas, fique à vontade para corrigir. Também estou aberto a sugestões de melhoria ou qualquer feedback que me ajude a melhorar.

Minhas Redes

GitHub: https://github.com/sougabriel

LinkedIn: https://www.linkedin.com/in/sougabriels/

--

--

Gabriel de Souza
Gabriel de Souza

Written by Gabriel de Souza

Apenas escrevo algumas coisas que aprendi durante minha jornada como desenvolvedor, nada de mais! Eu sempre busco me aprimorar e seu feedback pode me ajudar.

No responses yet