Startec

Startec

Usando variáveis de CSS de maneira dinâmica com angular · lucasnsantos

Mai 21, às 16:33

·

5 min de leitura

·

0 leituras

Imagem Capa Introdução Como engenheiros de software sabemos que existem inúmeras maneiras de resolvermos um problema. Sabemos também que existem boas ou não tão boas soluções e que elas p...
Usando variáveis de CSS de maneira dinâmica com angular · lucasnsantos

Imagem Capa

Introdução

Como engenheiros de software sabemos que existem inúmeras maneiras de resolvermos um problema. Sabemos também que existem boas ou não tão boas soluções e que elas podem ser impactadas por diversos fatores como falta de tempo, baixo conhecimento da tecnologia utilizada no projeto, falta de documentação técnica e de negócio.

Compartilho com vocês uma solução que pode não ser a melhor, mas foi, a solução que atendeu bem a demanda impactada pela perda de conhecimento técnico e de negócio da jornada que precisava do desenvolvimento de uma funcionalidade bem pontual.


Funcionalidade

A funcionalidade em si era bem tranquila, precisávamos alterar algumas cores dos componentes de uma jornada do nosso sistema, com base em uma informação do usuário.

Após efetuarmos uma análise mais aprofundada nos projetos, identificamos que seria muito complicado implementarmos uma lógica que permitisse alterar de forma dinâmica os componentes.

Escolhemos trazer a responsabilidade da mudança dos componentes para o projeto principal. Começamos a desenhar algumas soluções, como a jornada foi desenvolvida em Angular, inicialmente consideramos criar vários arquivos de SCSS específicos para serem carregados, sempre que o usuário, com a informação necessária para troca dos componentes, efetuasse login em nosso sistema.


Uma pequena observação sobre essa jornada: ela faz parte de um projeto em uma arquitetura de “micro-frontend”, logo, essa abordagem foi desenvolvimento levando essa ponto em consideração.

Implementando Service

O dado que precisamos é disponibilizado via um BFF (Backend for Frontend) desenvolvido para essa jornada, implementamos um novo endpoint, que retorna se o cliente contém ou não a informação que precisamos para efetuarmos as alterações no componente.

Com endpoint implementado, o próximo passo foi desenvolver o service, que tem a responsabilidade de chamar o serviço sempre que fosse necessário.

@Injectable({ providedIn: 'root' })
export class ComponentService {
 constructor(
 private http: HttpClient,
 ) {}
 getComponentInfo(): Observable<ComponentModel> {
 return this.http.get<ComponentModel>('url do BFF');
 }
}

Implementando Guard

No nosso “micro-forntend” implementamos um guard, que fica responsável por solicitar ao BFF a informação do cliente, sempre que o usuário acessa uma rota específica em nosso sistema.

@Injectable({
 providedIn: 'root',
})
export default class ComponentGuard implements CanActive {
 constructor(
 private readonly componentService: ComponentService,
 ) { }
 canActive(route: ActivatedRouteSnapshot, state: RouterStateSnapshot):
 boolean | UrlTree | Observable<boolean | UrlTree> | Promise<boolean | UrlTree> {
 const keyName = localStorage.getItem('keyName');
 if (!keyName) {
 this.componentService.getItem().subscribe((componentType: boolean) => {
 localStorage.setItem('keyName', componentType.keyName);
 styleProperties.map((style: IStyleProperties) => {
 document.documentElement.style.setProperty(
 `--${style.nameProperty}`,
 `${componentType.type ? style.newValueProperty : style.defaultValueProperty}`
 );
 });
 });
 } else {
 styleProperties.map((style: IStyleProperties) => {
 document.documentElement.style.setProperty(
 `--${style.nameProperty}`,
 `${keyName ? style.newValueProperty : style.defaultValueProperty}`
 );
 });
 }
 return true;
 }
}

Agora entraremos um pouco mais em detalhes da implementação.


Detalhes do Guard

Verificamos que o dado não está armazenado no localStorage, efetuamos uma nova chamada para BFF, para saber se o usuário possui ou não a informação que precisamos.

this.componentService.getItem().subscribe((componentType: boolean) => {});

Depois, adicionamos novamente no localStorage.

localStorage.setItem('keyName', componentType.keyName);

Criamos um arquivo de type bem simples, que contém informações de tipagem dos objetos que usamos para criação das variáveis de forma dinâmica.

export type IStyleProperties = {
 nameProperty: string,
 newValueProperty: string,
 defaultValueProperty: string,
}

Também, criamos um arquivo que contém todos os dados que precisamos para efetuar a customização na nossa jornada.

import { IStyleProperties } from 'IStyleProperties';
export const styleProperties: IStyleProperties[] = [
 {
 nameProperty: 'component-class',
 componentMain: '#000',
 componentDefault: '#fff'
 },
 ...
];

Partimos agora para o trecho código do nosso guard, responsável por adicionar as variáveis de CSS. Usando a função map, pegamos todos os dados do arquivo de propriedades e criamos todas as variáveis necessárias para customização dos componentes.

styleProperties.map((style: IStyleProperties) => {
 document.documentElement.style.setProperty(
 `--${style.nameProperty}`,
 `${componentType.type ? style.newValueProperty : style.defaultValueProperty}`
 );
});

O document.documentElement retorna o Elemento que é o elemento raiz do documento (por exemplo, o elemento <html> para documentos HTML).

O setProperty() define um novo valor para uma propriedade em um objeto de declaração de estilo CSS.

No arquivo app.component.scss recuperamos os valores das variáveis e alteramos o que precisamos dos componentes, para esse exemplo alteramos as fontes, background e algumas cores.

:host ::ng-deep component-selector .component-class {
 background-color: var(--component-class-background-color);
 color: var(--component-class-color);
 font-size: var(--component-class-font-size)
}
:host ::ng-deep other-component-selector .other-component-class {
 border-radius: var(--other-component-class-border-radius)
}

O seletor :host, seleciona o nódulo raiz do shadow DOM, que no caso pode ser representado por ```. No exemplo ele é responsável por mudar a cor do fundo.

A aplicação da pseudoclasse ::dg-deep a qualquer regra CSS desativa completamente o encapsulamento de exibição dessa regra. Qualquer estilo com ::dg-deep aplicado torna-se um estilo global.

Por fim, implementamos o método localStorage.removeItem('keyName'); para limparmos o localStorage sempre que o usuário desloga do sistema.


Conclusão

Meu objetivo em trazer esse case é mostrar que muitas vezes pensamos sempre em entregar a melhor solução possível, para área de negócio e os usuários finais, porém, algumas dessas vezes por questões tecnológicas e por falta de conhecimento do projeto, precisamos entregar a melhor solução naquele contexto que estamos inseridos.

Espero que gostem do conteúdo, deixem o seu like, comentem e compartilhem a opinião de vocês, com dicas e sugestões de melhorias.

~> LinkedIn


Referências


Continue lendo

DEV

Authentication system using Golang and Sveltekit - Dockerization and deployments
Introduction Having built out all the features of our application, preparing it for deployment is the next step so that everyone around the world will easily access it. We will deploy our apps (backend and...

Hoje, às 19:52

DEV

LEARN API AND ITS MOST POPULAR TYPE
An API (Application Programming Interface) is a set of rules and protocols that allows different software applications to communicate and interact with each other. It defines the methods, data structures, and...

Hoje, às 19:26

AI | Techcrunch

Investors take note: Wildfire smoke will spark a surge in East Coast climate tech startups
As smoke from Canadian wildfires has enveloped large swathes of the East Coast, millions of people have found themselves trapped inside, gazing out on orange skies and hazy cityscapes. The air quality index —...

Hoje, às 18:08

DEV

A Plain English Guide to Reverse-Engineering the Twitter Algorithm with LangChain, Activeloop, and DeepInfra
Imagine writing a piece of software that could understand, assist, and even generate code, similar to how a seasoned developer would. Well, that’s possible with LangChain. Leveraging advanced models such as...

Hoje, às 18:08

DEV

Finding Harmony in Marketing and UX
When we think of teamwork in the world of user experience (UX), we often imagine design and engineering working together. However, the idea of design and marketing working together is not as common. While...

Hoje, às 17:02

DEV

💡 Where to Find Inspiration for Building Your Next App
The first steps before turning your ideas into code. Whenever I’m trying to think of an idea to build a new application or website and I get stumped on what to do, there’s one phrase that always comes to...

Hoje, às 16:58

DEV

How to create 700+ SEO optimised pages for website in 1 h using Next.JS, OpenAI, Postgres
Small intro, I started learning coding couple of months before and since then experimenting with different small side projects. So this I show coding still looks for me:) What did I build this...

Hoje, às 16:37

DEV

Angular Project Mongodb database Connect | Angular Website Project | Angular App
Angular Project Mongodb database Connect | Angular Website Project | Angular App - YouTube ​ @softwaretechit Download Our App:- https://blog.softwaretechit.com/p/download.htmlWhat will we Learn In This...

Hoje, às 16:10

AI | Techcrunch

Meta warned it faces 'heavy sanctions' in EU if it fails to fix child protection issues on Instagram
The European Union has fired a blunt warning at Meta, saying it must quickly clean up its act on child protection or face the risk of “heavy sanctions”. The warning follows a report by the Wall Street...

Hoje, às 16:03

DEV

Taking Control with PostgreSQL Functions: Closing the Gap to ORM Functionality
Unveiling the Disparity: Understanding the Divide Between Direct Driver and ORM Functionality When it comes to choosing the technologies for developing a backend and manipulating data in a database like...

Hoje, às 16:02