Novidades no Angular 17
Introdução
A comunidade Angular recebeu com entusiasmo a tão aguardada versão 17, que transformou significativamente o framework, ampliando o foco em desempenho e na experiência da pessoa desenvolvedora, o que levou essa atualização a ser conhecida como "Angular Renaissance", ou seja, Renascimento Angular.
Vamos conhecer o Angular 17 e como ele busca otimizar a experiência do desenvolvimento em Angular?
Identidade Visual e nova documentação
Para refletir a evolução do framework, a equipe do Angular lançou uma nova identidade visual, que segundo eles representa o futuro dessa ferramenta, projetado para significar a evolução do Angular para um framework moderno.
Além disso, também foi lançada uma nova documentação, o angular.dev. Este novo site apresenta uma estrutura renovada, guias atualizados e conteúdo aprimorado, cheio de tutoriais incorporados implementados com WebContainers e até mesmo um Playground.
A atual versão do Angular.dev é uma prévia beta, a antiga documentação do Angular ainda está funcionando, mas a equipe está planejando tornar o Angular.dev o site padrão do Angular na versão 18.
Agora vamos mergulhar nos recursos da versão 17 do Angular!
Control flow integrado
A partir de pesquisas a equipe do Angular identificou que muitas pessoas desenvolvedoras ainda passavam por perrengues com a sintaxe *ngIf
, *ngSwitch
, e *ngFor
.
Pensando na melhoria da experiência da pessoa desenvolvedora, eles lançaram uma nova sintaxe para blocos de renderização de templates mais otimizada e intuitiva em que, internamente, o compilador Angular transforma essa sintaxe em instruções JavaScript que executam o control flow, o lazy loading e muito mais.
Além disso, essa mudança é mais um passo em direção ao objetivo de remover por completo a biblioteca zone.js
, que já começou através do uso dos Signals na v16. Com o uso das diretivas *ngIf
, *ngSwitch
, e *ngFor
, seria muito complexo remover por completo essa biblioteca.
A nova sintaxe se aproxima muito mais do JavaScript, por isso é mais intuitiva e exige menos pesquisas de documentação, além de ter uma melhor verificação de tipos, graças a um type narrowing otimizado, melhorias de desempenho e está automaticamente disponível para uso nos templates sem exigir importações adicionais.
Declarações condicionais
Para observarmos a mudança de sintaxe, criamos um componente para renderizar a seguinte lista de tópicos de estudo:
listaDeEstudos: String[] = ['HTML', 'CSS', 'JavaScript', 'TypeScript', 'Angular', 'React', 'Vue'];
Nesse componente, precisaremos utilizar uma estrutura condicional para, se houver itens na lista, exibir a quantidade. Se não houverem itens, a mensagem “ A lista está vazia!” deve aparecer.
Fazendo isso com *ngIf
na versão 16 do Angular, vamos obter o seguinte código:
A seguir, temos a estrutura de um *ngIf
:
<section *ngIf="listaDeEstudos.length > 0; else listaVazia">
<p> A lista tem {{ listaDeEstudos.length }} itens</p>
</section>
<ng-template #listaVazia>
<p>A lista está vazia!</p>
</ng-template>
Observe que foi preciso estruturar um ng-template
para o caso da condição não ser verdadeira. O código também segue uma sintaxe muito específica do Angular, o que dificulta o entendimento para quem vai ter seu primeiro contato com o framework.
Com a instrução if
built-in, esse bloco condicional será semelhante a:
@if (listaDeEstudos.length > 0) {
A lista tem {{ listaDeEstudos.length }} itens
} @else {
A lista está vazia!
}
O código se torna mais limpo, conciso e mais compreensível para quem não conhece o Angular mas já teve contato com JavaScript, enquanto obtemos o mesmo resultado desejado de exibição da quantidade de itens na lista.
A possibilidade de fornecer o conteúdo do @else
de forma direta simplifica muito a escrita em relação ao *ngIf
, além de que a nova sintaxe permite o uso do @else if
, o que anteriormente era impossível.
Também conseguimos observar essas melhorias no *ngSwitch
. Para isso criamos uma variável nivelEstudante
do tipo String que poderá ser definida como “iniciante” ou “intermediario” em nosso componente.
Caso seja definida como “Iniciante”, devemos exibir a mensagem “Comece por HTML, CSS e JS”. Caso seja definida como “intermediario”, vamos exibir a mensagem “Escolha seu framework!”. Como caso padrão, vamos exibir a mensagem “Vamos estudar?”.
Observe como isso ficaria com o *ngSwitch
na versão 16:
<div [ngSwitch]="nivelEstudante">
<p *ngSwitchCase="'iniciante'"> Comece por HTML, CSS e JS </p>
<p *ngSwitchCase="'intermediario'"> Escolha seu framework! </p>
<p *ngSwitchDefault> Vamos estudar? </p>
</div>
Com a nova sintaxe, o mesmo código se transforma em algo mais sucinto e semelhante ao JavaScript, como percebemos no exemplo à seguir:
@switch (nivelEstudante) {
@case ('iniciante') { <p> Comece por HTML, CSS e JS </p> }
@case ('intermediario') { <p> Escolha seu framework! </p> }
@default { <p> Vamos estudar? </p> }
}
Loop for integrado
Um problema comum no desempenho nas aplicações é observado pela falta de função trackBy
no *ngFor
. Por isso, a nova sintaxe do loop @for
garante que o track seja obrigatório para garantir um desempenho melhor.
O novo loop também tem o atalho @empty
para exibição de coleções que não tenham nenhum item. Mas o maior destaque vem para o novo algoritmo de diffing, que é mais otimizado em relação ao *ngFor
, com um tempo de execução até 90% mais rápido.
Vamos aplicar esse loop ao nosso componente de lista de estudos para entender seu funcionamento. Usando o *ngFor
, vamos listar os tópicos de estudo na tela:
<ul *ngFor="let conteudo of listaDeEstudos">
<li>{{ conteudo }}</li>
</ul>
Como comentamos anteriormente, o *ngFor
não obriga que seja usado um parâmetro “track”. Esse parâmetro é necessário para renderizar e atualizar os itens da lista de forma eficiente, pois é uma função que retorna um identificador único para cada item da lista, ajudando o Angular a rastrear quais itens foram adicionados, removidos ou reordenados na lista, e evitando a renderização desnecessária de itens inalterados. Isso pode melhorar o desempenho e o uso de memória da aplicação, especialmente ao lidar com listas grandes ou dinâmicas.
Por isso, esse é um ponto que chama atenção na nova sintaxe do for
. Aplicando a renderização da lista de estudos no novo padrão da versão 17, podemos visualizar o uso do track
:
<ul>
@for (conteudo of listaDeEstudos; track $index) {
<li>{{ conteudo }}</li>
} @empty {
<p>Preencha a lista para visualizar itens!</p>
}
</ul>
Por fim, o control flow integrado já está disponível como prévia para desenvolvedores na versão 17 e um dos objetivos do projeto do time Angular é viabilizar uma migração totalmente automatizada. Para testar essa funcionalidade em seus projetos existentes, você pode utilizar o seguinte comando de migração:
ng generate @angular/core:control-flow
Deferrable views
O lazy loading é uma técnica importante quando criamos páginas web pois tem como objetivo carregar recursos apenas quando são necessários. No contexto do Angular, isso significa carregar módulos ou componentes somente quando são requisitados, melhorando o tempo de carregamento inicial da aplicação.
A versão 17 do Angular trouxe uma nova ferramenta para otimizar esse processo: as deferrable views ou visualizações adiáveis. Esse mecanismo pode ser utilizado para tornar seus apps mais rápidos e com melhor desempenho.
A estrutura se baseia em um bloco de código adiável @defer
que pode ser acompanhado de um bloco @placeholder
, que vai conter um conteúdo que será exibido enquanto o bloco adiado não estiver disponível, um bloco @loading
que traz a mensagem de carregamento e um bloco @error
para exibição em caso de erro.
Para exemplificar o uso desta ferramenta, vamos utilizar o projeto que utilizamos nos exemplos anteriores, a lista de estudos.
No projeto criamos um novo componente chamado conteudo-estudos, que será uma página com texto e imagens. No nosso componente de lista, adicionamos um botão que leva para o novo componente.
Nesse novo componente, temos um grande volume de texto e algumas imagens:
O template desse componente está assim:
<h1>Título</h1>
<p>
<!-- blocos de texto lorem ipsum omitidos -->
</p>
<img src="../../assets/logo-html.png" alt="Logo do HTML" width="100px" height="100px"><br>
<img src="../../assets/logo-css.png" alt="Logo do CSS" width="100px" height="100px"><br>
<img src="../../assets/logo-javascript.png" alt="Logo do JavaScript" width="100px" height="100px"><br>
Podemos utilizar o bloco @defer
para fazer o carregamento sob demanda dessas imagens e otimizar o desempenho da página. Esse bloco permite receber diferentes parâmetros para o controle do carregamento, sendo o parâmetro padrão idle
, que será carregado apenas quando o navegador atingir a inatividade:
<!-- @defer em modo idle -->
@defer{
<img src="../../assets/logo-html.png" alt="Logo do Angular" width="100px" height="100px"><br>
}
Ou seja, no bloco acima a imagem será exibida quando o navegador tiver carregado todo o conteúdo e ficado inativo. Assim, o adiamento no carregamento da imagem fica quase imperceptível:
Para manipularmos as outras imagens, vamos conhecer alguns outros parâmetros e, para entendermos melhor, selecionei a opção de “slow 3g” na aba network
das ferramentas do desenvolvedor. Assim, conseguiremos observar como o @defer
faz diferença no melhor desempenho em redes mais lentas.
No modo on viewport
, a imagem só será carregada quando estiver em um ponto visível da tela. Observe:
<!-- modo on viewport -->
@defer (on viewport) {
<img src="../../assets/logo-css.png" alt="Logo do Angular" width="100px" height="100px"><br>
} @placeholder {
<p>Futura imagem</p>
}@loading (minimum 2s) {
<p>Carregando imagem...</p>
}
No exemplo acima, o Angular primeiro renderiza o conteúdo do bloco de espaço reservado. A imagem apenas começa a ser carregada quando descemos a barra de rolagem até onde ela está localizada e ela entra em visualização. É possível observar seu carregamento lento através do bloco @loading
com a mensagem “Carregando imagem…” que é exibido em um tempo de no mínimo 2 segundos antes da imagem ser carregada. Assim que o carregamento é concluído, o Angular renderiza a imagem.
Agora, caso queiramos estabelecer um tempo para que a imagem seja carregada, podemos recorrer ao on timer
. Vamos aplicar esse recurso na última imagem, o logo do JS:
<!-- modo on timer -->
@defer (on timer(4s)) {
<img src="../../assets/logo-javascript.png" alt="Logo do Angular" width="100px" height="100px">
} @placeholder {
<p>Futura imagem</p>
}@loading {
<p>Carregando imagem...</p>
} @error {
Carregamento falhou!
}
Observe o comportamento da última imagem (logo do JavaScript) sendo carregada com o on timer
:
Nesse caso serão contados 4 segundos até que a imagem seja carregada. Enquanto isso, o placeholder que definimos como “Futura imagem” é exibido.
E esses são apenas alguns dos parâmetros que podem ser aplicados nas deferrable views. Para conhecer outros, observe os detalhes na nova documentação do Angular clicando aqui.
Usamos as imagens como exemplo na melhoria de desempenho, mas as deferrable views suportam o carregamento sob demanda de componentes, diretivas e pipes e qualquer CSS associado e podem ser extremamente úteis em cenários distintos como para tratar a exibição de dados retornados de APIs ou para controlar a renderização de componentes mais pesados como animações 3D, por exemplo. Fazer o lazy loading de um componente quando um elemento DOM é exibido poderia gerar uma lógica grande e complexa. Assim, o uso do de deferrable views torna muito mais simples e prática a utilização da API IntersectionObservers
.
SSR / Experiência de renderização híbrida renovada
O Server-Side Rendering (SSR) é um passo importante na otimização do desempenho de páginas Angular, permitindo que as páginas HTML sejam geradas no servidor e enviadas ao navegador do usuário já prontas.
No Angular v17, temos um novo prompt que te pergunta se deseja habilitar server-side rendering (SSR) e static-site generation (SSG/pre rendering) diretamente ao criar um novo projeto com ng new
. Mas como isso impacta o desempenho da aplicação e porque essa era uma das novidades mais esperadas do Angular 17?
Essa abordagem resolve problemas de desempenho associados à renderização exclusiva no lado do cliente (Client-Side Rendering), em que o navegador recebe um documento HTML básico e, em seguida, executa o JavaScript para preencher dinamicamente o conteúdo da página, o que pode levar a um atraso na renderização.
Por isso, com SSR as páginas chegam ao navegador já renderizadas, melhorando a indexação de busca e otimizando a experiência do usuário, especialmente em conexões mais lentas.
Por isso, a Angular CLI agora inclui a opção para habilitar SSR no momento da criação do projeto. Além disso, ainda temos o comando alternativo ng new nome-do-app --ssr
.
Hydration
Outra novidade é que agora hydration ou hidratação, que no contexto de desenvolvimento web consiste na transformação do conteúdo HTML inicialmente processado no servidor, convertendo-o em uma página web altamente interativa no lado do cliente, não está mais em preview. Ou seja, agora esse recurso está habilitado por padrão em novos apps que usem server-side rendering.
Novo pacote @angular/ssr
O repositório do Angular Universal foi movido para o repositório do Angular CLI, o que representa uma incorporação mais profunda da renderização no lado do servidor (SSR) nas ferramentas Angular.
A partir de agora, para integrar suporte à renderização híbrida em sua aplicação existente, basta executar o seguinte comando:
ng add @angular/ssr
Ao adotar o comando ng add @angular/ssr
, ocorre a ativação automática de hydration juntamente com a inclusão da capacidade de compilação SSR e SSG. O @angular/ssr
proporciona funcionalidades equiparáveis às do @nguniversal/express-engine
, que está atualmente em modo de manutenção. Caso esteja utilizando o express-engine
, a atualização automática do código para @angular/ssr
será realizada pelo Angular CLI.
Deploy de apps SSR
A equipe Angular também colaborou com provedores de nuvem para facilitar o deploy em suas plataformas, visando aprimorar a experiência da pessoa desenvolvedora. Assim, graças à prévia antecipada de sua nova CLI que reconhece frameworks, o Firebase agora reconhecerá e fará o deploy automático de aplicações Angular com praticamente nenhuma configuração.
firebase experiments:enable webframeworks
firebase init hosting
firebase deploy
Já para quem prefere ferramentas nativa, o AngularFire permite o deploy no Firebase com ng deploy
:
ng add @angular/fire
ng deploy
Novos hooks de ciclo de vida
A equipe Angular desenvolveu um conjunto de novos hooks de ciclo de vida para aprimorar o desempenho de SSR (server-side rendering) e SSG (static-site generation) no Angular a longo prazo. O objetivo é se afastar da emulação e das manipulações diretas no DOM. Ao mesmo tempo, ao longo do ciclo de vida da maioria das aplicações, é comum a necessidade de interagir com elementos.
Esses novos hooks oferecem uma solução viável para equilibrar a otimização de desempenho e a demanda contínua de interação com elementos durante o desenvolvimento de aplicações:
afterRender: registra um callback para ser invocado sempre que a aplicação termina a renderização.
afterNextRender: registra um callback para ser invocado na próxima vez que a aplicação terminar a renderização.
Suporte experimental de view transitions
A API View Transitions proporciona transições fluidas durante as alterações no DOM. No Angular router, agora há suporte direto para essa API por meio do recurso withViewTransitions
. Ao utilizá-lo, você pode tirar proveito das capacidades nativas do navegador para criar transições animadas entre rotas.
É possível incorporar esse recurso à sua aplicação hoje, configurando-o na declaração do provider do router na inicialização. Vamos exemplificar aplicando esse recurso ao nosso projeto de Lista de Estudos. No arquivo main.ts
, vamos adicionar a configuração:
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app/app.component';
import { routes } from './app/app.routes';
import { provideRouter, withViewTransitions } from '@angular/router';
//alguns imports omitidos
bootstrapApplication(AppComponent, {
providers: [
provideRouter(routes, withViewTransitions()),
]
});
Na inicialização da aplicação, no provider de rotas, passamos o array de rotas que foi configurado em app.routes.ts
(lembre-se de importar), seguido do recurso withViewTransitions()
. Também é importante lembrar de importar o provideRouter
e withViewTransitions
de @angular/router
.
Em seguida, no arquivo styles.css
vamos escrever a animação que queremos aplicar na transição de rotas. Aqui eu defini a seguinte:
@keyframes fade-in{
from{
opacity: 50%;
}
}
@keyframes fade-out{
to{
opacity: 0;
}
}
@keyframes slide-right{
from{
transform: translateX(33px);
}
}
@keyframes slide-left{
to{
transform: translateX(-33px);
}
}
::view-transition-old(root){
animation: 1s fade-out, 1s slide-left;
}
::view-transition-new(root){
animation: 2s fade-in, 1s slide-right;
}
Quando houver a transição de rotas, ::view-transition-old
será responsável pela animação do que estava sendo exibido, enquanto ::view-transition-new
cuidará da nova exibição. O resultado da animação das rotas é o seguinte:
O withViewTransitions
também aceita um objeto de configuração opcional com uma propriedade onViewTransitionCreated
, que é um callback que proporciona controles adicionais. Isso inclui a capacidade de decidir se deseja pular animações específicas, adicionar classes ao documento para personalizar a animação e remover essas classes quando a animação for concluída, entre outras opções.
Signal-based components
Os signals, que aprendemos a usar na versão 16, agora saem da preview para desenvolvedores, com exceção da função effect
, que permanecerá em fase de prévia por enquanto a fim de melhorar sua semântica.
O Angular introduziu uma significativa mudança no seu framework com o novo sistema reativo baseado em Signals. Agora, é possível designar um componente como Signal-based adicionando a propriedade signal: true
no decorator @Component
. Essa abordagem pode substituir os decorators @Input
e @Output
, proporcionando uma maneira mais centrada nos Signals e simplificando consideravelmente o controle da reatividade, já que estratégias de verificação de mudanças, como o ngOnChanges
, podem ser substituídas. A principal vantagem é o controle total sobre as mudanças proporcionado pelos Signals.
Temos um artigo detalhado explicando o funcionamento dos Signals. Caso queira explorar mais sobre os Signals no Angular, você pode acessar o artigo Entendendo os Signals do Angular clicando aqui.
Standalone APIs
Desde a versão 16 do Angular já havia uma movimentação para implementação de Standalone APIs, com o objetivo de simplificar a experiência de desenvolvimento, diminuir a quantidade de código repetitivo e reduzir a dependência dos NgModules
.
Assim, é possível marcar componentes, diretivas e pipes como standalone: true
. As classes Angular marcadas como standalone não precisam ser declaradas em um NgModule
e emitem um erro caso declaradas.
Os componentes standalone especificam suas dependências diretamente, em vez de obtê-las por meio de NgModules. Por exemplo, no nosso projeto de exemplo, o componente ListarEstudosComponent
é standalone, por isso ele pode importar diretamente outro componente standalone, como o ConteudoEstudosComponent
:
@Component({
selector: 'app-listar-estudos',
standalone: true,
imports: [ConteudoEstudosComponent],
templateUrl: './listar-estudos.component.html',
styleUrl: './listar-estudos.component.css'
})
export class ListarEstudosComponent {
//lógica do componente
}
Sendo assim, as Standalone APIs, que incluem componentes, diretivas e pipes autônomos, estão agora habilitadas em todas as novas aplicações do Angular. Todos os comandos ng generate
agora criarão estruturas para esses elementos standalone.
Além disso, nas documentações (tanto angular.io como angular.dev) houveram atualizações quanto a esse tema para garantir consistência na experiência de aprendizado, práticas de desenvolvimento e boas práticas.
Os NgModules
serão mantidos por enquanto, mas a equipe recomenda fortemente a migração gradual para as novas Standalone APIs, e para isso disponibilizaram um guia de migração para facilitar esse processo, que você pode acessar clicando aqui.
Conclusão
O Angular 17 representa uma verdadeira renascença para o framework. Com melhorias visuais, aprimoramentos de desempenho e novos recursos, a comunidade Angular está pronta para uma experiência de desenvolvimento mais moderna e eficiente. As inovações introduzidas na identidade visual, documentação, control flow, SSR, e outros aspectos refletem o compromisso contínuo da equipe Angular em atender às necessidades da comunidade de desenvolvedores. O futuro do Angular parece empolgante, e a versão 17 é um passo significativo nessa jornada de evolução.
Nesse artigo, trouxemos os maiores destaques da v17 do Angular, mas caso você queira se aprofundar mais e ver as demais atualizações desse framework, acesse este artigo no Blog Angular.
Caso você queira explorar o projeto com os exemplos que trouxemos neste artigo, pode acessar o repositório no GitHub clicando aqui.
Você se interessa por Angular e não vê a hora de mergulhar no Angular 17? Então não perca as novidades da Alura, muito em breve teremos uma formação fresquinha abordando essas novidades e te ajudando a evoluir ainda mais! 🚀