Publicado em
- 7 minutos de leitura
Angular 20: Conheça as novidades!

Angular v20: Novidades e Aprimoramentos
A versão 20 do Angular introduz um conjunto de novas funcionalidades, aprimoramentos na experiência de desenvolvimento e a estabilização de recursos previamente introduzidos.
Esta release visa consolidar a plataforma e otimizar o fluxo de trabalho para desenvolvedores.
Estabilização de APIs Reativas e de Renderização
A v20 marca a promoção à estabilidade de diversas APIs fundamentais:
-
Reactivity Primitives: As APIs
effect
,linkedSignal
etoSignal
alcançaram o status de estável, oferecendo mecanismos robustos para a gestão de estado reativo.import { signal, effect } from '@angular/core' const counter = signal(0) effect(() => { console.log(`Current count: ${counter()}`) }) counter.update((n) => n + 1)
-
Incremental Hydration: A estratégia de hidratação progressiva do client-side agora é considerada estável, configurável via
provideClientHydration(withIncrementalHydration())
. A diretiva@defer
nos templates permite o carregamento e hidratação sob demanda.import { provideClientHydration, withIncrementalHydration } from '@angular/platform-browser' // ... provideClientHydration(withIncrementalHydration())
Nos templates você pode usar a diretiva
@defer
para hidratar partes do template sob demanda.@defer (hydrate on viewport) { <shopping-cart /> }
-
SSR Route-Level Configuration: A capacidade de definir a estratégia de renderização no servidor por rota (
RenderMode
) é agora uma API estável, configurada através da propriedadedata.renderMode
nas definições de rota.export const routeConfig: ServerRoute = [ { path: '/login', mode: RenderMode.Server }, { path: '/dashboard', mode: RenderMode.Client }, { path: '/product/:id', mode: RenderMode.Prerender, async getPrerenderParams() { const dataService = inject(ProductService) const ids = await dataService.getIds() // ["1", "2", "3"] // `id` is used in place of `:id` in the route path. return ids.map((id) => ({ id })) } } ]
Novo Recurso experimental
Agora, vamos supor que estamos recebendo dados de uma conexão WebSocket.
Para este propósito, podemos usar um recurso de streaming:
@Component({
template: `{{ dataStream.value() }}`
})
export class App {
// Lógica de inicialização do WebSocket irá aqui...
// ...
// Inicialização do recurso de streaming
dataStream = resource({
stream: () => {
return new Promise<Signal<ResourceStreamItem<string[]>>>((resolve) => {
const resourceResult = signal<{ value: string[] }>({
value: []
})
this.socket.onmessage = (event) => {
resourceResult.update((current) => ({
value: [...current.value, event.data]
}))
}
resolve(resourceResult)
})
}
})
}
Nesse exemplo minimalista, declaramos um novo recurso de streaming, que retorna uma Promise
de um Signal.
O Signal tem um tipo de valor ResourceStreamItem<string[]>
, que significa que o Signal pode ter o valor { value: string[] }
ou {error: … }
em caso de erro.
Emitimos os valores recebidos sobre a conexão WebSocket via o Signal resourceResult
.
Agora, vamos criar um exemplo mais interessante, usando o recurso httpResource
experimental:
@Component({
template: `{{ userResource.value() | json }}`
})
class UserProfile {
userId = signal(1)
userResource = httpResource<User>(() => `https://example.com/v1/users/${this.userId()}`)
}
O snippet acima irá enviar uma solicitação HTTP GET para a URL especificada sempre que o userId mudar.
O httpResource
retorna um HttpResourceRef
que tem uma propriedade value do tipo Signal que podemos acessar diretamente no template.
O userResource
tem outros valores, como isLoading, headers e outros, como se vê abaixo.
Por baixo dos panos, o httpResource
usa o HttpClient
, portanto você pode especificar interceptores no provedor de HttpClient
:
bootstrapApplication(AppComponent, {
providers: [provideHttpClient(withInterceptors([loggingInterceptor, cachingInterceptor]))]
})

Zoneless Change Detection em Developer Preview
O modelo de detecção de mudanças sem a dependência de Zone.js avança para o status de developer preview, habilitado através de provideZonelessChangeDetection()
.
import { bootstrapApplication } from '@angular/platform-browser'
import { AppComponent } from './app/app.component'
import { provideZonelessChangeDetection, provideBrowserGlobalErrorListeners } from '@angular/core'
bootstrapApplication(AppComponent, {
providers: [provideZonelessChangeDetection(), provideBrowserGlobalErrorListeners()]
})
Além disso, é possível adicionar o zoneless na criação de um novo projeto com o comando
ng new
:
Adições à Sintaxe de Templates
A linguagem de templates do Angular foi expandida com novos operadores e funcionalidades:
-
Suporte a untagged template literals para interpolação de strings.
<img [src]="`/assets/images/user-${userId()}.png`" />
-
Implementação dos operadores aritmético de exponenciação (
**
) e lógicoin
.<div>{{ base ** exponent }}</div> <div *ngIf="'propertyName' in object">Property exists</div>
-
Introdução do operador
void
para descarte explícito de valores de retorno de event listeners.import { Directive, HostListener } from '@angular/core' @Directive({ host: { '(click)': 'void handleClick()' } }) export class NoPreventDefaultDirective { handleClick(): boolean { console.log('Handling click.') return false // Without void, this would call preventDefault } }
Aprimoramentos na Experiência de Desenvolvimento
-
Implementação de type checking para host bindings, configurável via
typeCheckHostBindings
noangularCompilerOptions
dotsconfig.json
.{ "angularCompilerOptions": { "typeCheckHostBindings": true } }
No gif abaixo, vemos um exemplo de uso da validação de
typeCheckHostBindings
. -
Refinamento da API do componente
NgComponentOutlet
através da introdução de inputs declarativos.import { Component, Input, Type } from '@angular/core' import { NgComponentOutlet } from '@angular/common' interface DynamicData { message: string } @Component({ selector: 'app-dynamic-component', template: `<p>{{ data.message }}</p>` }) export class DynamicComponent { @Input() data!: DynamicData } @Component({ selector: 'app-root', template: ` <ng-container [ngComponentOutlet]="dynamicComponentType" [ngComponentOutletInputs]="{ data: dynamicInput }" ></ng-container> ` }) export class AppComponent { dynamicComponentType: Type<DynamicComponent> = DynamicComponent dynamicInput: DynamicData = { message: 'Data from parent component' } }
-
Para criar dinamicamente um componente Angular, você pode usar a função
createComponent
.Na versão 20, foi introduzido novos recursos que permitem aplicar diretivas e especificar associações a componentes criados dinamicamente:
import { createComponent, signal, inputBinding, outputBinding } from '@angular/core' const canClose = signal(false) const title = signal('Título do meu diálogo') // Criar MyDialog createComponent(MyDialog, { bindings: [ // Vincular um signal ao input `canClose`. inputBinding('canClose', canClose), // Ouvir especificamente o evento `onClose` no diálogo. outputBinding<Result>('onClose', (result) => console.log(result)), // Cria vinculação bidirecional com a propriedade title twoWayBinding('title', title) ], directives: [ // Aplicar a diretiva `FocusTrap` ao `MyDialog` sem quaisquer associações. FocusTrap, // Aplicar a diretiva `HasColor` ao `MyDialog` e vincular o valor `red` ao seu input `color`. // O callback para `inputBinding` é invocado em cada detecção de mudança. { type: HasColor, bindings: [inputBinding('color', () => 'red')] } ] })
Acima, criamos um componente de Dialog e especificamos:
- Vinculamos um signal ao input
canClose
da Dialog. - Escutamos o evento
onClose
usnado um callback que registra o resultado emitido. - Criamos two-data binding entre a input
title
da Dialog e o signaltitle
usando a funçãotwoWayBinding
.
Além disso, adicionamos as diretivas
FocusTrap
eHasColor
ao componente.Observe que também podemos vincular valores para inputs da diretiva
HasColor
que aplicamos aoMyDialog
. - Vinculamos um signal ao input
Suporte experimental para vitest
Com a depreciação do Karma, o Angular CLI agora oferece suporte experimental para o vitest como um substituto para testes automatizados.
Para usar Vitest, execute:
npm i vitest jsdom --save-dev
Atualize o arquivo angular.json
:
"test": {
"builder": "@angular/build:unit-test",
"options": {
"tsConfig": "tsconfig.spec.json",
"buildTarget": "::development",
"runner": "vitest"
}
}
Será atualizar também os testes do projeto para utilizar as funções do Vitest:
...
import { describe, beforeEach, it, expect } from 'vitest';
...
Atualizações no Guia de Estilo
O guia de estilo do Angular foi atualizado para modernizá-lo e simplificá-lo, com base no feedback da comunidade (RFC). As principais mudanças incluem:
- Remoção de práticas de “code health” não específicas do Angular.
- Movimentação de melhores práticas do Angular (não relacionadas a estilo) para a documentação.
- Sufixos de nomes de arquivos e classes tornaram-se opcionais para reduzir código repetitivo e incentivar nomes mais intencionais.
A partir do Angular v20, o Angular CLI não gerará sufixos por padrão. Projetos existentes podem habilitá-los via ng update
.
Para novos projetos, a geração de sufixos pode ser ativada com a seguinte configuração no angular.json
:
"projects": {
"app": {
// ...
"schematics": {
"@schematics/angular:component": { "type": "component" },
"@schematics/angular:directive": { "type": "directive" },
"@schematics/angular:service": { "type": "service" },
"@schematics/angular:guard": { "typeSeparator": "." },
"@schematics/angular:interceptor": { "typeSeparator": "." },
"@schematics/angular:module": { "typeSeparator": "." },
"@schematics/angular:pipe": { "typeSeparator": "." },
"@schematics/angular:resolver": { "typeSeparator": "." }
},
// ...
}
}
Refletindo a evolução do Angular, a maior parte das orientações sobre NgModules
foi removida.
O uso de @HostBinding
e @HostListener
foi revisto, favorecendo o objeto host nos metadados da diretiva, e lacunas no suporte a host binding foram corrigidas.
Breaking Changes e Deprecações
A atualização para a v20 implica em algumas alterações que exigem atenção:
-
Atualização das peer dependencies para Node.js, TypeScript e RxJS.
-
Remoção da emissão de atributos
ng-reflect-*
em modo de desenvolvimento. -
Remoção da API
InjectFlags
.import { inject, Optional, Host } from '@angular/core' import { ParentComponent } from './parent.component' export class ChildComponent { parent = inject(ParentComponent, { optional: true, host: true }) }
-
Depreciação do suporte oficial ao HammerJS.
Conclusão
O Angular v20 representa um marco significativo na evolução contínua do framework, focando na estabilização de recursos poderosos, na modernização da experiência de desenvolvimento e na preparação para o futuro com inovações como a detecção de mudanças sem Zone.js
.
As melhorias nas APIs reativas e de renderização, juntamente com as adições à sintaxe de templates e o suporte experimental ao Vitest
, demonstram o compromisso da equipe do Angular em fornecer uma plataforma robusta, eficiente e agradável para os desenvolvedores.
Embora a atualização traga algumas breaking changes e deprecações, os benefícios em termos de performance, produtividade e alinhamento com as práticas modernas de desenvolvimento web certamente compensarão o esforço de migração.
O Angular v20 consolida sua posição como um framework de ponta, pronto para enfrentar os desafios dos projetos web mais exigentes.
Assine nossa Newsletter
Receba novos posts como esse na sua caixa de e-mail!
Sobre o autor
