Os textos descrevem uma modernização de projetos corporativos Angular ao longo de versões recentes, culminando no Angular 22 (anunciado em 3 de junho de 2026) e, em paralelo, em mudanças introduzidas principalmente no Angular 21 para melhorar legibilidade e desempenho. A série sobre migração relata a adoção de inject() para reduzir boilerplate e melhorar a escalabilidade em arquiteturas com classes base e herança, além de permitir padrões funcionais como interceptores HTTP em forma de funções que injetam dependências quando necessário. Nos templates, os artigos apontam a substituição de *ngIf/*ngFor por Control Flow nativo (@if/@for) e o uso declarativo de @defer para carregamento sob demanda de componentes pesados, reduzindo impacto no carregamento inicial. Em arquitetura, a migração organiza o código por domínios (features) em vez de por tipos técnicos, facilitando manutenção e evolução. No Angular 22, os destaques incluem consolidação de OnPush como padrão (com Default renomeado para Eager), APIs de Resource para dados reativos, Signal Forms em produção, melhorias de DI com @Service e injectAsync, debounce nativo para Signals, incremental hydration ligado por padrão e mudanças no HttpClient para FetchBackend (com upload exigindo XHR). Também são citadas otimizações voltadas a microfrontends, como bootstrap com configuração, shadow roots e opções de roteamento e limpeza de injeção por rota.
Angular v21–v22: Signals, Control Flow, inject() e mudanças para microfrontends
Os textos descrevem uma modernização de projetos corporativos Angular ao longo de versões recentes, culminando no Angular 22 (anunciado em 3 de junho de 2026) e, em paralelo, em mudanças introduzidas...
- O Angular adota novos padrões para reatividade e desempenho, com mudança para OnPush como comportamento padrão em componentes novos no Angular 22.
- O projeto passa a usar inject() para injeção de dependências fora do construtor, reduzindo boilerplate e facilitando herança e refatorações.
- Templates evoluem para Control Flow nativo (@if/@for) e carregamento sob demanda com @defer para componentes pesados.
- No Angular 22, recursos e formulários reativos ganham consolidação: Resource API (incluindo httpResource) e Signal Forms entram como estáveis para produção.
- Há mudanças de base no HttpClient no Angular 22: FetchBackend passa a ser padrão e upload com progresso continua dependendo de XHR.
Se você acompanha a evolução do framework do Google nos últimos anos, sabe que ele vem passando por uma reconstrução silenciosa — peça por peça. Com o lançamento do Angular 22 em 3 de junho de 2026, essa reconstrução deixou de ser uma promessa e virou padrão. Não estamos olhando para mais uma leva de features experimentais: estamos olhando para a consolidação de um ecossistema inteiramente repensado. Para quem vive de aplicações enterprise, Clean Architecture e ecossistemas de Microfrontends, essa é a versão que finalmente entrega o que vinha sendo prometido desde o Angular 16: um framework reativo de ponta a ponta, zone-less por natureza, e com muito menos cerimônia no caminho. Os experimentos acabaram. A seguir, o que mudou de verdade — e o que você precisa fazer antes de rodar o ng update. 📖 Se você está começando: vários termos técnicos deste artigo (change detection, Signals, SSR, injeção de dependência, microfrontends…) estão explicados num glossário no final. Leia o artigo de ponta a ponta e use o glossário como consulta sempre que bater uma dúvida. O que chegou estável no Angular 22 OnPush é o novo padrão de change detection (o antigo Default virou Eager e está depreciado). Resource API estável: resource, rxResource e httpResource prontos para produção. Signal Forms estável, com Submission API, schemas dinâmicos (Zod/Valibot) e interop com Reactive Forms. Novo decorator @Service(), encurtando o @Injectable({ providedIn: 'root' }). injectAsync para injeção de dependência lazy, com prefetch via onIdle. debounced para debounce nativo em Signals/Resources. Incremental Hydration ligada por padrão. HttpClient usa FetchBackend por padrão (withFetch() depreciado). Melhorias importantes de Router e bootstrap pensadas para Microfrontends. 1. OnPush como novo padrão de Change Detection Chegou o momento que a comunidade pedia desde sempre: o ChangeDetectionStrategy.OnPush agora é o comportamento padrão de qualquer componente novo. A decisão faz todo sentido num mundo signals-first — quem usa Signals já recebe notificações cirúrgicas sobre o que mudou, e o OnPush aproveita isso ao máximo, verificando apenas os componentes realmente afetados em vez de varrer a árvore inteira. O antigo Default (que checava a árvore toda) foi renomeado para Eager e está depreciado. Se você ainda precisa do comportamento antigo em algum componente, declare explicitamente: import { ChangeDetectionStrategy, Component } from '@angular/core'; @Component({ selector: 'app-legacy', changeDetection: ChangeDetectionStrategy.Eager, // substitui o antigo 'Default' template: `...`, }) export class LegacyComponent {} Detalhe crítico para a migração: durante o ng update, se o Angular não encontrar uma estratégia explícita, ele aplica Eager automaticamente para não quebrar nada. Ou seja, você não ganha performance "de graça" — precisa migrar componente a componente para colher o benefício do OnPush. 2. Resource API e httpResource estáveis A Resource API era a peça que faltava no quebra-cabeça dos Signals: derivar dados assíncronos de forma reativa, normalmente disparando requisições HTTP quando um Signal muda. Agora resource, rxResource e httpResource estão estáveis e liberados para produção. O ponto de entrada mais confortável é o httpResource. Ele recebe uma lambda reativa que retorna a requisição: se um Signal usado lá dentro muda, a requisição é refeita automaticamente. import { httpResource } from '@angular/common/http'; import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { Flight } from './flight'; @Component({ selector: 'app-flight-search', changeDetection: ChangeDetectionStrategy.OnPush, template: ` @if (flightsResource.isLoading()) { <div>Carregando…</div> } @else if (flightsResource.error()) { <div>Erro: {{ flightsResource.error() }}</div> } @else { @for (flight of flightsResource.value(); track flight.id) { <app-flight-card [item]="flight" /> } } `, }) export class FlightSearch { protected readonly filter = signal({ from: 'São Paulo', to: 'Uberlândia' }); protected readonly flightsResource = httpResource<Flight[]>( () => ({ url: 'https://api.exemplo.io/flight', params: { from: this.filter().from, to: this.filter().to }, }), { defaultValue: [] }, // evita o componente lidar com `undefined` na inicialização ); protected reload(): void { this.flightsResource.reload(); } } O recurso gerencia o próprio estado via Signals: value, error, isLoading e um status mais detalhado (idle, loading, reloading, error, resolved, local). E o melhor: race conditions são tratadas automaticamente — se várias requisições chegam em sequência, só o resultado da mais recente é usado, exatamente como o switchMap faria no RxJS, mas sem você escrever uma linha de pipe. Quer pular a requisição sob certas condições? Basta retornar undefined na lambda. 3. Signal Forms prontos para produção Acabou a eterna divisão entre Reactive Forms e Template-Driven. Os Signal Forms saíram do status experimental e são a abordagem recomendada para formulários: declarativos, fortemente tipados e reativos por Signals. O coração da API é a função form, que recebe um Signal com os dados e um schema de validação: import { form, minLength, required } from '@angular/forms/signals'; protected readonly flightForm = form(this.flight, (path) => { required(path.from); required(path.to); required(path.date); minLength(path.from, 3); }); O resultado é um FieldTree: uma estrutura aninhada de Signals em que cada campo expõe value, dirty, invalid e errors. No template, você usa a diretiva FormField: <input [formField]="flightForm.from" id="flight-from" /> <div>{{ flightForm.from().errors() | json }}</div> E não para aí. O Angular 22 (somando a 21.1 e 21.2) trouxe um stack de formulários surpreendentemente completo: Submission API (FormRoot + submit): toda a lógica de envio dentro do próprio form, inclusive recebendo erros de validação do servidor de volta para o estado do formulário. Schemas dinâmicos com validateStandardSchema, compatível com Zod e Valibot — e que reavaliam quando um Signal muda. Classes CSS condicionais (ng-valid, ng-invalid, ng-dirty…) via provideSignalFormsConfig. Interop com Reactive Forms via compatForm e SignalFormControl, então você migra de forma incremental sem reescrever o mundo. 4. O novo decorator @Service() Uma daquelas melhorias de ergonomia que você agradece todo dia. O @Service() encurta o caso mais comum de injeção — aquele @Injectable({ providedIn: 'root' }) repetido à exaustão: import { Service } from '@angular/core'; @Service() export class FlightClient { // Provido em root por padrão. A intenção fica explícita. } Se você não quer o provimento automático em root, desligue com autoProvided: false e proveja manualmente (em app.config.ts, no componente ou na rota): @Service({ autoProvided: false }) export class TabRegistry {} Importante: o @Service() não aposenta o @Injectable(). Ele é um atalho para o caso mais frequente. Onde você usa configurações de provider mais sofisticadas, o @Injectable() continua sendo a ferramenta certa. Numa Clean Architecture, o @Service() deixa a camada de adaptadores de infraestrutura bem mais enxuta — mas use com critério, não como substituto cego. 5. injectAsync: injeção de dependência lazy Esse é um presente para quem briga com tamanho de bundle e tempo de startup. Com injectAsync, você injeta uma dependência só quando ela é realmente necessária — ideal para serviços que carregam bibliotecas pesadas e só entram em cena após uma ação específica do usuário: import { injectAsync } from '@angular/core'; @Component({ /* ... */ }) export class CheckinPage { private readonly upgradeService = injectAsync(() => import('./upgrade-service').then((m) => m.UpgradeService), ); protected async upgrade(): Promise<void> { const service = await this.upgradeService(); service.upgrade(/* ... */); } } O import — e, portanto, o carregamento do bundle — só acontece na primeira chamada. Para evitar o atraso dessa primeira vez, dá para pré-carregar com a opção prefetch combinada com onIdle (que carrega quando o navegador está ocioso): import { injectAsync, onIdle } from '@angular/core'; private readonly upgradeService = injectAsync( () => import('./upgrade-service').then((m) => m.UpgradeService), { prefetch: onIdle }, // carrega quando o browser estiver ocioso ); Só lembre: para o lazy load funcionar, o serviço injetado precisa ser auto-provido (via @Service() ou @Injectable({ providedIn: 'root' })). 6. debounced: debounce nativo para Signals Signals, por natureza, não sabem nada sobre tempo — não têm debounceTime nem throttle. O Angular 22 resolve isso com a função debounced, que cria um Resource cujo valor é atualizado com o atraso definido: import { debounced } from '@angular/core'; const filter = signal(''); const debouncedFilter = debounced(filter, 300); // 300ms effect(() => console.log(debouncedFilter.value())); Para formulários, o debounce já vem embutido nos Signal Forms — o debounced cobre todo o resto. 7. Incremental Hydration ligada por padrão Foco em performance: a hidratação incremental agora vem habilitada por padrão via provideClientHydration(). Para aplicações com SSR, isso significa um ganho real no carregamento inicial, hidratando componentes sob demanda em vez de tudo de uma vez. Se por algum motivo você não quiser, desligue explicitamente com withNoIncrementalHydration() — e há uma schematic de migração para ajudar. 8. HttpClient agora usa FetchBackend por padrão Mudança discreta, mas com impacto real na migração: o HttpClient passou a usar a Fetch API por padrão, então o withFetch() está depreciado e pode ser removido. A pegadinha está no progresso de requisições. O antigo reportProgress foi substituído por duas opções dedicadas — e upload progress exige XHR: // Download (funciona com Fetch) http.get('/arquivo-grande', { reportDownloadProgress: true, observe: 'events' }); // Upload (exige withXhr()) http.post('/upload', file, { reportUploadProgress: true, observe: 'events' }); Se você usa reportUploadProgress com o FetchBackend, o Angular lança uma exceção de propósito, sinalizando que você precisa de withXhr(). Felizmente, o ng update adiciona withXhr() automaticamente para preservar o comportamento existente. 9. Para quem vive de Microfrontends Essa seção raramente aparece nos resumos genéricos, mas é justamente o que importa para quem orquestra Module Federation e shells distribuídos. O Angular 22 trouxe melhorias pensadas para esse cenário: ApplicationRef.bootstrap com config: o bootstrap() agora aceita um objeto de configuração análogo ao createComponent, permitindo subir um microfrontend sob demanda numa área específica da página: appRef.bootstrap(MyComponent, { hostElement: document.querySelector('#root')! }); Bootstrap sob Shadow Roots: dá para iniciar o Angular diretamente dentro de uma shadow root, com os estilos registrados corretamente no SharedStylesHost. Mais um passo rumo a uma integração limpa com Web Components. Rotas wildcard com segmentos à frente e atrás ('foo/**/bar'): antes só com path matcher customizado. Perfeito para shells que precisam carregar o microfrontend certo a partir de um padrão de URL. Auto cleanup de Environment Injectors por rota (withExperimentalAutoCleanupInjectors): serviços providos no nível da rota finalmente são destruídos ao sair dela, em vez de viverem até a aplicação fechar. Ainda experimental, mas resolve uma dor antiga de vazamento de instâncias (memory leaks). Melhores práticas para migrar com segurança Migrar aplicações escaláveis exige estratégia. Um roteiro pragmático: Rode o ng update sem medo do OnPush. Ele aplica Eager onde não havia estratégia explícita, então nada quebra. Depois, migre componente a componente para OnPush e Signals, começando pelas camadas de apresentação mais "quentes". Limpe a camada de injeção de dependência. Use o refactor da sua IDE para trocar os @Injectable({ providedIn: 'root' }) mais simples por @Service() — e mantenha @Injectable() onde há providers customizados. Adote injectAsync + onIdle para enxugar o bundle inicial. Identifique serviços pesados que não são necessários no startup e carregue-os sob demanda, com prefetch no idle. Migre GETs simples de RxJS para httpResource. Onde você usava RxJS só para resolver uma requisição GET, o httpResource reduz a complexidade cognitiva drasticamente — sem perder o tratamento de race conditions. Confira o HttpClient. Remova withFetch() (agora redundante) e garanta withXhr() onde você depende de progresso de upload. O ng update ajuda, mas revise. Valide a hidratação no SSR. A Incremental Hydration vem ligada; teste o comportamento e use withNoIncrementalHydration() se algum fluxo específico precisar do modo antigo. Para quem está começando — por onde começar de verdade: não tente migrar tudo de uma vez. Rode o ng update, garanta que o app continua funcionando (graças ao fallback Eager), e só então escolha um componente simples para converter para Signals + OnPush. Sinta o ganho, ganhe confiança, repita. Migração boa é a chata e gradual — não a heroica. Considerações finais O Angular 22 é a prova de que o framework do Google se reconstruiu de dentro para fora. Aquela fama de "pesado" e "verboso" ficou para trás: hoje temos reatividade fina, zone-less de verdade e uma Developer Experience de altíssimo nível. Quem segurou as atualizações nos últimos meses agora recebe uma base estável que permite migrar para um desenvolvimento baseado em Signals sem depender de APIs experimentais. Se você está começando agora, a boa notícia é que está entrando num Angular muito mais simples e direto do que o de alguns anos atrás. E se você já constrói produtos complexos e escaláveis, poucas vezes tivemos ferramentas tão poderosas à disposição. Gostou da análise? Aplica essas ideias no seu próximo refactor e me conta como foi. Para continuarmos discutindo arquitetura frontend, conteúdo técnico e o mundo do desenvolvimento, se inscreve no canal GhabDev no YouTube e acompanha meus próximos artigos por aqui. Bons códigos! 💻🚀 📚 Glossário — termos técnicos do artigo Para quem está começando (ou quer revisar). Em ordem alfabética. Assíncrono: algo que não acontece na hora — você pede um dado ao servidor e a resposta chega "depois". O código precisa saber esperar. Boilerplate: código repetitivo e cerimonioso que você escreve toda hora sem ele agregar lógica de verdade. Menos boilerplate = menos código chato. Bootstrap: o "dar partida" da aplicação Angular — o momento em que o framework inicializa e renderiza o primeiro componente na página. Bundle: o pacote final de JavaScript que o navegador baixa para rodar seu app. Quanto maior, mais lento o primeiro carregamento. Change detection (detecção de mudanças): o mecanismo que decide quando e onde o Angular re-renderiza a tela depois que algum dado muda. Clean Architecture: um jeito de organizar o código em camadas bem separadas (regras de negócio no centro, detalhes como banco e API nas bordas). O objetivo é trocar uma tecnologia sem bagunçar o resto. Debounce: técnica para esperar o usuário parar antes de reagir. Ex.: numa busca, em vez de chamar a API a cada letra digitada, você espera 300ms de silêncio e só então busca. Economiza requisições e melhora a experiência. Declarativo: você descreve o que quer ("este campo é obrigatório") em vez de escrever passo a passo como fazer. Mais legível. Decorator: aquele @ antes de uma classe (@Component, @Service). É uma anotação que diz ao Angular "trate esta classe de um jeito especial". dirty: marca se o usuário já mexeu naquele campo de formulário. Útil para só mostrar o erro "campo obrigatório" depois que ele interagiu, em vez de gritar erro com o formulário ainda em branco. Eager / Default: a estratégia "antiga e cara" de change detection — a cada ciclo o Angular varre a árvore inteira de componentes procurando mudanças. Funciona, mas desperdiça processamento. No Angular 22 virou Eager e está depreciada. Enterprise: aplicações grandes, de empresas, com muitos times e anos de manutenção pela frente. Fetch vs. XHR: duas formas de o navegador fazer requisições HTTP. O XHR (XMLHttpRequest) é o jeito antigo; o Fetch é o moderno, baseado em Promises (mais limpo) e melhor para streaming. A única coisa que o Fetch ainda não faz bem é relatar progresso de upload. Fortemente tipado: o TypeScript "sabe" o formato exato dos seus dados e te avisa no editor se você errar um campo, antes de rodar. Menos bug em produção. Framework: uma "caixa de ferramentas" estruturada que já resolve problemas comuns (rotas, formulários, requisições) para você não reinventar a roda. Hydration / Incremental Hydration: o processo de "dar vida" ao HTML estático vindo do servidor — o Angular conecta os eventos e a interatividade. Incremental significa fazer isso aos poucos, só nas partes que o usuário vê/usa, em vez de tudo de uma vez. Injeção de dependência (DI): em vez de a sua classe criar sozinha as coisas de que precisa (ex.: um serviço de API), ela apenas pede e o Angular entrega. Deixa o código mais fácil de testar e manter. Interop / migração incremental: "interop" é a capacidade de o código novo conversar com o antigo. "Migração incremental" é atualizar o projeto aos poucos, um pedaço de cada vez, sem reescrever tudo num único e arriscado big bang. Lazy loading (carregamento sob demanda): em vez de baixar tudo de uma vez, você adia o download de partes pesadas para o momento em que forem realmente usadas. A página abre mais rápido. Memory leak (vazamento de memória): quando algo continua ocupando memória mesmo depois de não ser mais necessário. Em apps longos, vazamentos acumulam e deixam tudo lento. Microfrontends: dividir uma aplicação grande em vários "mini-apps" independentes, cada um tocado por um time diferente, que se juntam numa só tela. É o "microsserviços" do mundo frontend. Module Federation: a tecnologia (do Webpack) que permite a esses mini-apps compartilharem código e serem carregados em tempo de execução. ng update: comando do Angular CLI que atualiza seu projeto de uma versão para outra, aplicando migrações automáticas quando possível. onIdle / prefetch: "prefetch" é carregar algo antes de precisar, de forma discreta. O onIdle faz isso nos momentos em que o navegador está ocioso (usando a API requestIdleCallback). Assim, quando o usuário clicar, já está tudo pronto. OnPush: estratégia "econômica" de change detection — o Angular só verifica o componente quando recebe uma entrada nova, um evento, ou um Signal muda. Resultado: mais performance. providedIn: 'root': diz que existe uma única instância do serviço para o app inteiro (padrão singleton). Antes você escrevia isso à mão; agora o @Service() já assume por padrão. Race condition (condição de corrida): quando o usuário digita rápido e dispara várias buscas seguidas, a resposta de uma busca antiga pode chegar depois e sobrescrever o resultado certo com um errado. O httpResource resolve isso sozinho. Reativo / reatividade: a UI reage sozinha quando os dados mudam, sem você mandar "atualiza a tela" manualmente. Reactive Forms vs. Template-Driven: as duas formas antigas de fazer formulário no Angular. Reactive Forms monta o formulário no TypeScript (mais controle, mais verboso); Template-Driven monta no HTML (mais simples, menos poderoso). Os Signal Forms unificam o melhor dos dois. Requisição HTTP: o pedido que seu app faz a um servidor ("me dá a lista de voos"). O servidor responde com os dados. Resource: um "pacote inteligente" que faz a requisição, guarda o resultado e te entrega de bandeja três estados: carregando, erro e o valor final. RxJS / switchMap: o RxJS é uma biblioteca de programação reativa baseada em streams (fluxos de dados ao longo do tempo). O switchMap é um operador dela que, quando chega um pedido novo, cancela o anterior — exatamente o que evita resultados bagunçados. Schema: a "planta" que descreve as regras de validação de um formulário (quais campos, quais limites). Zod e Valibot são bibliotecas populares para isso. Schematic: um script de automação do Angular que aplica mudanças no seu código para você (renomear, mover configs, etc.) durante uma migração. Service (serviço): uma classe que concentra lógica reutilizável e sem tela — ex.: buscar dados, fazer cálculos, guardar estado. Vários componentes podem compartilhar o mesmo serviço. Shell: o app "casca" que orquestra e carrega os microfrontends dentro de si. Signals: a forma moderna do Angular guardar um valor que avisa automaticamente quem depende dele quando muda — tipo uma planilha onde a célula B se recalcula sozinha quando A muda. SSR (Server-Side Rendering): o servidor monta o HTML já pronto e manda para o navegador. O usuário vê a página rápido, antes mesmo do JavaScript carregar. Bom para performance e SEO. Startup: o tempo entre o usuário abrir a página e ela ficar utilizável. Web Components / Shadow Root: Web Components são componentes de UI que funcionam em qualquer framework. A shadow root é uma "bolha" isolada que protege os estilos do componente de vazarem (e de sofrerem interferência) — ótimo quando vários times misturam código. Wildcard (``):** um "coringa" na definição de rota que casa com qualquer caminho. Útil quando você não sabe de antemão a URL exata, só o padrão. Zone-less / Zone.js: o Zone.js é uma biblioteca que historicamente "vigiava" tudo que acontecia na aplicação para saber quando atualizar a tela. Zone-less é o Angular funcionando sem ela — algo possível graças aos Signals, e mais rápido. Fontes técnicas: anúncio oficial do Angular v22 (angular.dev) e a análise detalhada de Manfred Steyer (ANGULARarchitects), ambos de junho de 2026.
5 hours agoFala, comunidade dev! 👋 Chegamos ao nono e último artigo da nossa série documentando a evolução estrutural de um YMS (Yard Management System) gigante, saindo do Angular 16 direto para o 21. Já refatoramos o núcleo, as rotas, os Signals, a estrutura de pastas e a internacionalização. Mas, para o usuário final, a arquitetura invisível do código não importa. O que importa é a tela. E hoje, os usuários de sistemas corporativos B2B (Backoffice, ERPs, YMS) não aceitam mais interfaces cinzas com cara de Windows 95. Eles exigem a mesma fluidez, UX e beleza dos aplicativos que usam no celular. 1. A Evolução do PrimeNG e o Padrão Ouro dos Grids Nossa interface sempre foi alicerçada no PrimeNG. O pulo de 5 versões do Angular exigiu adequações estruturais pesadas na forma como lidávamos com os estilos. A biblioteca evoluiu muito para adotar um sistema de design baseado em tokens (CSS Variables) e uma integração profunda com Utility Classes (o PrimeFlex). Sistemas logísticos respiram dados. Tabelas (<p-table>) não são apenas para visualização; elas são a principal ferramenta de trabalho do operador. Implementamos recursos modernos que mudaram completamente a percepção de performance e usabilidade do usuário: Filtros e Ordenação no lado do Servidor: Absolutamente essencial quando você tem milhões de registros na base de dados e não pode travar o navegador tentando filtrar isso no Front-end. Scroll Virtual (Lazy Loading UI): Substituindo a paginação tradicional por um scroll infinito suave. O Angular destrói as linhas HTML que sobem pela tela e recria as que aparecem embaixo. Isso consome recursos de renderização da CPU apenas para as linhas que estão visíveis. Salvamento de Estado (Stateful Grids): O usuário reordena as colunas, ajusta as larguras, sai da tela e, ao voltar no dia seguinte, a tabela "lembra" exatamente da preferência dele usando o Local Storage. 2. A Inevitável Chegada do Dark Mode O Dark Mode (Modo Escuro) não é mais um "frufru" visual. Para operadores logísticos que passam 10 horas por dia olhando para painéis de controle e gráficos de pátio em salas escuras de monitoramento, é uma questão de saúde visual e ergonomia. A Mágica dos Tokens: Com a arquitetura de tokens CSS das versões modernas do Angular e do PrimeNG, a alternância de temas parou de exigir a recarga inteira da página ou a inserção manual de dezenas de classes no HTML com JavaScript. Agora, mudar para o tema escuro é apenas uma substituição de variáveis nativas CSS (como --surface-a ou --text-color) na raiz do documento (:root). A transição é instantânea e extremamente leve para o navegador. Conclusão: O Veredito da Jornada Pular 5 versões de um ecossistema complexo exige sangue frio, testes extensivos e muita leitura de changelogs. Mas o resultado final faz cada erro de compilação no terminal valer a pena: Um código absurdamente mais limpo (Adeus, app.module e ngOnDestroy!). Uma compilação instantânea no ambiente de desenvolvimento (graças ao novo bundler com Vite e esbuild). Uma aplicação rápida como um raio no navegador do usuário (com Signals e Zoneless). Manter sistemas atualizados não é apenas perseguir a novidade tecnológica pelo hype. É garantir agilidade e segurança para continuar entregando valor ao negócio nos próximos 5 anos, sem que o código vire um legado intocável que ninguém na equipe tem coragem de mexer. Espero que essa série de 9 artigos tenha ajudado a desmistificar alguns conceitos modernos do Angular e inspirado você a encarar a modernização das suas bases de código por aí. Um grande abraço e até a próxima refatoração! 🚀 Quais foram os maiores desafios de UI/UX que vocês já enfrentaram em sistemas corporativos internos? Deixem nos comentários! 👇
5 days agoFala, comunidade dev! 👋 Nas últimas 6 partes dessa série, mergulhamos fundo no código: Standalone Components, Rotas, Signals e Zoneless. Mas refatorar a arquitetura técnica sem arrumar a "casa" (a estrutura de pastas) é colocar motor de Ferrari em chassi de fusca. Um projeto de 5 anos geralmente sofre do mesmo mal genérico de organização, e hoje vou mostrar como aplicamos conceitos de Clean Architecture e Domain-Driven Design (DDD) no Front-end para salvar a manutenção do nosso YMS (Yard Management System). 1. O Erro Clássico: Organização por Tipo Técnico Lá no Angular 14/16, a literatura (e as ferramentas de CLI) nos ensinava a agrupar arquivos pelo que eles eram, e não pelo que eles faziam. A estrutura clássica de um sistema parecia assim: src/app/ ├── components/ (misturava botões com painéis de negócio) ├── models/ (centenas de interfaces jogadas aqui) ├── services/ (tudo que era API ficava aqui) └── views/ (as páginas em si) O Pesadelo Cognitivo: Para alterar a tela de "Agendamento", o desenvolvedor precisava abrir a pasta de views para achar o HTML, depois pular para a pasta de models para entender a interface, e depois caçar a chamada de API na pasta de services. O código que mudava junto morava em bairros completamente diferentes do projeto. 2. A Virada: Estrutura Orientada a Domínios (Features) Na migração para o Angular 21, pivotamos a arquitetura para domínios de negócio. A regra de ouro virou: se o código muda junto, ele deve morar junto. Nossa nova arquitetura isola responsabilidades. O módulo (agora Standalone) de balanca não faz a menor ideia do que acontece dentro de agendamento. src/app/ ├── core/ (Interceptors, Auth, Guards - estritamente técnico e Singleton) ├── shared/ (Botões, Pipes, componentes UI "burros" e reaproveitáveis) └── features/ (Os Domínios do Negócio) ├── agendamento/ │ ├── models/ │ ├── services/ │ └── components/ ├── balanca/ │ ├── models/ │ ├── services/ │ └── components/ 3. Por que isso muda o jogo? Organizar arquivos pode parecer preciosismo estético, mas em bases de código maduras e sistemas Enterprise, é a linha tênue entre entregar uma feature em 2 horas ou em 2 dias. Onboarding rápido: Um desenvolvedor júnior entra no time hoje para arrumar um bug na tela da Balança. Ele abre a pasta features/balanca e absolutamente tudo que ele precisa para entender aquele contexto está lá dentro. Escalabilidade e Desacoplamento: Se o negócio crescer absurdamente, nós podemos extrair a pasta balanca inteira e transformá-la em uma biblioteca independente (Angular Workspace/Nx) ou até mesmo em um Micro-frontend sem esforço, pois ela não compartilha "lixo" com outros domínios. Conclusão Ter um código moderno usando Signals e rodando sem o zone.js é maravilhoso, mas é a arquitetura de pastas que dita a velocidade e a sanidade mental do time no longo prazo. No próximo post (Parte 8), vamos abordar um desafio Enterprise raiz que costuma dar muita dor de cabeça em sistemas legados: a Internacionalização de dados e traduções dinâmicas. E como está a pasta src/app dos projetos de vocês hoje? Mais parecida com o primeiro modelo (técnico) ou com o segundo modelo (orientado a domínio)? Comentem aí! 👇
5 days agoFala, comunidade dev! 👋 No artigo anterior desta nossa jornada de migração de um sistema YMS (do Angular 16 para o 21), exploramos como a função inject() limpou as nossas classes TypeScript e resolveu o "inferno" dos construtores. Mas hoje, vamos fechar os arquivos .ts e olhar para os nossos templates HTML. O Angular passou por uma mudança drástica na forma como renderizamos o HTML. É hora de dar o adeus definitivo às velhas diretivas estruturais (*ngIf, *ngFor, *ngSwitch) e abraçar a nova sintaxe de Control Flow e o poderoso bloco @defer. 1. O Adeus ao CommonModule e à sintaxe pesada Durante anos, para fazer um simples if/else no Angular, precisávamos importar o CommonModule (ou as diretivas de forma isolada nos componentes Standalone) e lidar com a criação de <ng-template> para blocos else. Em uma tela de listagem de motoristas, o código clássico era assim: O Antes (Angular 16): <div *ngIf="motoristas.length > 0; else semMotoristas"> <ul> <li *ngFor="let motorista of motoristas; trackBy: trackPorId"> {{ motorista.nome }} </li> </ul> </div> <ng-template #semMotoristas> <p>Nenhum motorista encontrado na fila.</p> </ng-template> 2. O Novo Control Flow: Limpo, Nativo e Direto A partir do Angular 17, o framework introduziu uma nova sintaxe de bloco embutida diretamente no compilador. Não precisamos importar mais nada para fazer verificações lógicas. O código aproxima-se muito da fluidez de motores de template de outras linguagens (como o Razor ou o Blade). Olhem como o mesmo cenário acima fica agora, no Angular 21: O Depois (Angular 21): @if (motoristas().length > 0) { <ul> @for (motorista of motoristas(); track motorista.id) { <li>{{ motorista.nome }}</li> } </ul> } @else { <p>Nenhum motorista encontrado na fila.</p> } As grandes vitórias aqui: Menos Imports: O @if e o @for são nativos, reduzindo o boilerplate do componente. Track Obrigatório: O @for obriga a utilização da cláusula track. Isto previne um dos erros de performance mais comuns em projetos legados: renderizar listas gigantes de grids dinâmicas sem identificar os itens de forma unívoca, o que causava re-renderizações desnecessárias. Leitura Fluida: Acabaram os <ng-template> perdidos no final do arquivo apenas para gerenciar estados de else ou loading. 3. A Verdadeira Mágica em Sistemas Enterprise: O @defer Se o Control Flow melhora a Developer Experience (DX), o bloco @defer é um divisor de águas absoluto para a User Experience (UX) e Web Performance. Num sistema logístico complexo, temos telas que carregam gráficos pesados de dashboards, modais com mapas de pátios e componentes complexos do PrimeNG que só são usados se o usuário interagir com eles. Antigamente, dividir o carregamento destes componentes exigia código complexo e Lazy Loading manual. Agora, o Angular faz isso diretamente no HTML de forma declarativa. O Exemplo Prático: Imaginem um componente muito pesado de "Mapa de Pátio Logístico" que só deve ser carregado pelo navegador do usuário quando ele fizer scroll para baixo na página e o componente entrar na área visível (viewport): @defer (on viewport) { <app-mapa-patio-pesado [dados]="dadosPatio"></app-mapa-patio-pesado> } @placeholder { <p-skeleton width="100%" height="400px"></p-skeleton> } @loading { <div class="spinner-logistico">Carregando o mapa...</div> } O Esbuild/Vite (o nosso novo bundler) entende este @defer durante o build, corta o componente app-mapa-patio-pesado e coloca-o num arquivo .js separado. Este pedaço de código só trafega na rede quando o usuário precisa realmente dele. Reduzimos dezenas de megabytes no carregamento inicial da aplicação com algumas linhas de HTML! Conclusão Atualizar o HTML de um projeto com 5 anos de idade assusta, mas felizmente a equipe do Angular disponibilizou esquemas de migração automatizados (ng generate @angular/core:control-flow) que fazem o trabalho pesado por nós. Os novos templates deixaram as nossas views mais legíveis e deram-nos um controle de Lazy Loading granular sem paralelo. No próximo artigo (Parte 5), vamos unir o HTML limpo com um estado TypeScript impecável. Vamos falar de reatividade, do adeus ao velho ngOnDestroy e mergulhar fundo no uso de Signals! Já utilizaram o comando de migração automática de HTML nos projetos de vocês? Como foi a experiência de refatorar os antigos *ngIf e *ngFor? Compartilhem nos comentários! 👇
5 days agoFala, comunidade dev! 👋 Na segunda parte da nossa série sobre a modernização de um sistema corporativo (saindo do Angular 16 e indo direto para o 21), vimos como o roteamento ficou muito mais limpo com o fim das antigas classes Guards. Mas eu deixei um gancho no ar: essa limpeza nas rotas só foi possível graças a uma pequena função que mudou para sempre a forma como escrevemos Angular. Hoje vamos falar da verdadeira revolução silenciosa do framework: a função inject(). 1. O Pesadelo do Construtor em Sistemas Corporativos Quem trabalha com aplicações Enterprise sabe que uma única tela muitas vezes precisa se comunicar com diversas partes do sistema. Imagine um cenário clássico que todo desenvolvedor já enfrentou: uma tela complexa de Cadastro de Usuários. Para essa tela funcionar, você precisa buscar dados do usuário, validar o CEP em uma API externa, checar permissões, montar o formulário e gerenciar alertas na tela. No Angular 16, a única forma de trazer essas dependências para dentro do componente era através do constructor. O resultado era um bloco de código gigantesco, onde a injeção de dependência engolia a lógica de negócio: O Antes (Poluição visual no Angular 16): @Component({ ... }) export class CadastroUsuarioComponent { constructor( private usuarioService: UsuarioService, private viaCepService: ViaCepService, private permissoesService: PermissoesService, private fb: FormBuilder, private router: Router ) { } } Metade do arquivo do componente era apenas boilerplate (código repetitivo) para injetar dependências. Mas o problema real nem era a estética. O verdadeiro inferno começava quando precisávamos usar herança. 2. O Fim do "Inferno do super()" Em arquiteturas limpas, é extremamente comum criarmos classes base para reaproveitar lógica. Um exemplo perfeito é um BaseFormComponent que concentra a lógica de exibição de toasts de erro, spinners de loading e validações genéricas. A dor da herança antes do inject(): Se o seu BaseFormComponent precisasse injetar o MessageService e o LoadingService, todos os componentes filhos que estendessem essa classe precisavam injetar esses serviços em seus próprios construtores, apenas para passá-los para cima através da chamada do super(). // O componente filho precisava resolver dependências que não eram dele! constructor( private usuarioService: UsuarioService, private fb: FormBuilder, // O componente filho nem usa esses dois, mas precisa injetar para o pai! private messageService: MessageService, private loadingService: LoadingService ) { super(messageService, loadingService); } Se um dia você precisasse adicionar mais um serviço genérico na classe base, você quebrava a aplicação inteira. Era necessário abrir e refatorar o construtor de dezenas (ou centenas) de telas filhas. 3. A Chegada da Injeção Baseada em Propriedades Com a maturidade da função inject(), a injeção de dependências deixou de ser uma exclusividade do construtor e passou a ser declarada diretamente na inicialização das propriedades da classe. O Depois (Limpo e escalável no Angular 21): @Component({ ... }) export class CadastroUsuarioComponent extends BaseFormComponent { // A injeção agora vai direto na propriedade private usuarioService = inject(UsuarioService); private viaCepService = inject(ViaCepService); private fb = inject(FormBuilder); // O constructor sumiu! // O BaseFormComponent se vira sozinho para buscar suas próprias dependências. } Nessa nova abordagem, a classe base (BaseFormComponent) usa o inject() internamente para pegar seus próprios serviços de alerta e loading. O componente filho não precisa mais saber (nem repassar) o que o pai está consumindo. O acoplamento despenca, e a refatoração vira uma tarefa segura e simples. 4. Fora das Classes: Interceptors Funcionais Além de salvar a nossa sanidade na hora de estender classes, o inject() abriu as portas para o Angular ser muito mais funcional. Assim como as rotas, os velhos HttpInterceptors (que interceptam requisições de API e também eram classes verbosas) viraram funções puras. Em qualquer sistema corporativo, injetar o Bearer Token de autenticação em todas as chamadas HTTP é regra. Olha como esse código ficou absurdamente simples e direto agora: export const authInterceptor: HttpInterceptorFn = (req, next) => { const authService = inject(AuthService); // Injeção fora de uma classe! const token = authService.getToken(); const requestModificada = req.clone({ setHeaders: { Authorization: `Bearer ${token}` } }); return next(requestModificada); }; Conclusão Adotar o inject() em todo o projeto reduz drasticamente a quantidade de linhas de código, melhora a legibilidade e blinda o seu sistema contra refatorações dolorosas em classes base. É uma mudança de paradigma que separa os projetos legados engessados dos códigos modernos e fluidos. E por falar em reduzir código, no nosso próximo artigo da série (Parte 4), vamos sair do TypeScript e olhar para o HTML. Vamos dar o adeus definitivo ao *ngIf e ao *ngFor, e entender como o novo Control Flow e a mágica do @defer otimizam a renderização de telas pesadas. Como está a adoção do inject() nos projetos de vocês? Ainda preferem o bom e velho construtor ou já abraçaram as propriedades dinâmicas? Comentem aí! 👇
5 days ago
TechRadar publishes daily Quordle hints and answers for multiple dates
TechRadar runs a series of daily posts providing help for the word game Quordle, including hints and the solution for sp...
NYT Connections and Strands puzzles: hints and answers published across multiple outlets
Several outlets publish daily walkthroughs for The New York Times puzzle games, including NYT Connections and NYT Strand...
NYT Strands daily guides provide hints, clues, and answers for multiple dates
Multiple outlets publish daily coverage for the New York Times word game “Strands,” offering players guidance to solve e...