Angular v15 já está disponível!

Photo by Kimberly Farmer on Unsplash

Angular v15 já está disponível!

Alvaro Camillo Neto's photo
Alvaro Camillo Neto
·Nov 17, 2022·

12 min read

Table of contents

Tradução livre do artigo original: blog.angular.io/angular-v15-is-now-availabl..

No ano passado, foi removido o compilador legado e o pipeline de renderização do Angular, que permitiu o desenvolvimento de uma série de melhorias na experiência do desenvolvedor nos últimos meses. Angular v15 é o resultado disso com dezenas de refinamentos que levam a uma melhor experiência e desempenho do desenvolvedor.

Standalone API agora está estável e pronta para produção!

Na v14, foram introduzidas novas standalone APIs que permitem aos desenvolvedores criarem aplicativos sem usar NgModules. A equipe do Angular está feliz em compartilhar que essas APIs saíram da fase de preview e agora fazem parte da API estável do framework. A partir de agora ela será evoluída gradualmente seguindo o versionamento semântico.

Como parte de garantir que as standalone APIs estivessem prontas para serem utilizadas em produção, foram garantidos que os componentes standalone funcionem em todo ecosistema Angular e agora funcionem completamente com as APIs HttpClient, Angular Elements, router e mais.

As APIs standalone permitem que você inicialize um aplicativo usando um único componente:

import {bootstrapApplication} from '@angular/platform-browser';
import {ImageGridComponent} from'./image-grid';

@Component({
  standalone: true,
  selector: 'photo-gallery',
  imports: [ImageGridComponent],
  template: `
    … <image-grid [images]="imageList"></image-grid>
  `,
})
export class PhotoGalleryComponent {
  // component logic
}
bootstrapApplication(PhotoAppComponent);

Router and HttpClient tree-shakable standalone APIs

Você pode criar um aplicativo de multi-rotas usando as novas APIs standalone do router! Para declarar a rota raiz, você pode usar o seguinte exemplo:

export const appRoutes: Routes = [{
  path: 'lazy',
  loadChildren: () => import('./lazy/lazy.routes')
    .then(routes => routes.lazyRoutes)
}];

Onde lazyRoutes são declarados em:

import {Routes} from '@angular/router';

import {LazyComponent} from './lazy.component';

export const lazyRoutes: Routes = [{path: '', component: LazyComponent}];

e finalmente, registre o appRoutes na chamada bootstrapApplication:

bootstrapApplication(AppComponent, {
  providers: [
    provideRouter(appRoutes)
  ]
});

Outro benefício da API ProvideRouter é que ela pode ser tree-shakable! Os Bundlers podem remover recursos não utilizados do roteador em tempo de compilação. Em testes com a nova API, foi descoberto que a remoção desses recursos não utilizados do pacote resultou em uma redução de 11% no tamanho do código do router no bundle do aplicativo.

API de composição de diretivas

A API de composição de diretivas traz a reutilização de código para outro nível! Esse recurso foi inspirado pela solicitação de recurso mais popular no GitHub, solicitando a funcionalidade de adicionar diretivas a um elemento host.

A API de composição de diretivas permite que os desenvolvedores aprimorem os elementos do host com diretivas e adiciona ao Angular com uma poderosa estratégia de reutilização de código, que só é possível graças ao compilador do Angular. A API de composição de diretivas funciona apenas com diretivas independentes.

Vejamos um exemplo rápido:

@Component({
  selector: 'mat-menu',
  hostDirectives: [HasColor, {
    directive: CdkMenu,
    inputs: ['cdkMenuDisabled: disabled'],
    outputs: ['cdkMenuClosed: closed']
  }]
})
class MatMenu {}

No trecho de código acima, aprimoramos MatMenu com duas diretivas: HasColor e CdkMenu. MatMenu reutiliza todas as entradas, saídas e lógica associada com HasColors e apenas a lógica e as entradas selecionadas do CdkMenu.

Esta técnica pode lembrá-lo de herança múltipla ou traços em algumas linguagens de programação, com a diferença de que temos um mecanismo para resolução de conflitos de nomes e é aplicável a primitivas de interface de usuário.

A diretiva de imagem agora está estável!

Foi anunciada a preview da diretiva de imagem Angular que foi desenvolvida em colaboração com o Chrome Aurora na v14.2.

image.png

Agora ela está estável! A Land's End experimentou esse recurso e observou 75% de melhoria no LCP em um teste no lighthouse.

A versão v15 também inclui alguns novos recursos para a diretiva de imagem:

  • Geração automática de srcset: a diretiva garante que uma imagem de tamanho apropriado seja solicitada gerando o atributo srcset para você. Isso pode reduzir o tempo de download de suas imagens.

  • Modo de preenchimento [experimental]: este modo faz com que a imagem preencha seu contêiner pai, eliminando a necessidade de declarar a largura e a altura da imagem. É uma ferramenta útil se você não souber os tamanhos de suas imagens ou se quiser migrar imagens de plano de fundo CSS para usar a diretiva.

Você pode usar a diretiva NgOptimizedImage diretamente em seu componente ou NgModule:

import { NgOptimizedImage } from '@angular/common';

// Include it into the necessary NgModule
@NgModule({
  imports: [NgOptimizedImage],
})
class AppModule {}

// ... or a standalone Component
@Component({
  standalone: true
  imports: [NgOptimizedImage],
})
class MyStandaloneComponent {}

Para usá-lo em um componente, basta substituir o atributo src da imagem por ngSrc e certifique-se de especificar o atributo de prioridade para suas imagens LCP.

Você pode encontrar mais informações na documentação.

Guarda de rotas funcionais

Junto com as APIs de router independentes e tree-shakable, foi trabalhado o boilerplate das guardas de rota. Vejamos um exemplo onde definimos uma guarda que verifica se o usuário está logado:

@Injectable({ providedIn: 'root' })
export class MyGuardWithDependency implements CanActivate {
  constructor(private loginService: LoginService) {}

  canActivate() {
    return this.loginService.isLoggedIn();
  }
}

const route = {
  path: 'somePath',
  canActivate: [MyGuardWithDependency]
};

LoginService implementa a maior parte da lógica e na guarda só invocamos isLoggedIn(). Mesmo que o guard seja bem simples, temos muito boilerplate.

Com as novas guarda de rotas funcionais, você pode refatorar esse código para:

const route = {
  path: 'admin',
  canActivate: [() => inject(LoginService).isLoggedIn()]
};

Expressamos toda a guarda dentro da sua declaração. As guardas funcionais também podem ser compostas — você pode criar funções do tipo factory que aceitam uma configuração e retornam uma função de guarda ou resolver. Você pode encontrar um exemplo para executar proteções de roteador em série no GitHub.

O roteador desempacota as importações padrão

Para tornar o roteador mais simples e reduzir ainda mais o boilerplate, o roteador agora desempacota automaticamente as exportações padrão durante o carregamento lazy.

Vamos supor que você tenha o seguinte LazyComponent:

@Component({
  standalone: true,
  template: '...'
})
export default class LazyComponent { ... }

Antes dessa alteração, para fazer o carregamento lazy de um componente independente , você precisava:

{
  path: 'lazy',
  loadComponent: () => import('./lazy-file').then(m => m.LazyComponent),
}

Agora o roteador irá procurar por uma exportação padrão e se encontrar, usará automaticamente, o que simplifica a declaração de rota para:

{
  path: 'lazy',
  loadComponent: () => import('./lazy-file'),
}

Melhores stack traces

A equipe do Angular recebe muitos insights das pesquisas anuais com desenvolvedores, por isso queremos agradecer a você por dedicar um tempo para compartilhar seus pensamentos! Entramos de cabeça nas dificuldades com a experiência de depuração que os desenvolvedores enfrentam, descobrimos que as mensagens de erro poderiam ser melhoradas.

image.png

Problemas de depuração para desenvolvedores Angular

Foi feita uma parceria com o Chrome DevTools para corrigir isso! Veja um exemplo de stack trace que você pode trabalhar em um aplicativo Angular:

ERROR Error: Uncaught (in promise): Error
Error
    at app.component.ts:18:11
    at Generator.next (<anonymous>)
    at asyncGeneratorStep (asyncToGenerator.js:3:1)
    at _next (asyncToGenerator.js:25:1)
    at _ZoneDelegate.invoke (zone.js:372:26)
    at Object.onInvoke (core.mjs:26378:33)
    at _ZoneDelegate.invoke (zone.js:371:52)
    at Zone.run (zone.js:134:43)
    at zone.js:1275:36
    at _ZoneDelegate.invokeTask (zone.js:406:31)
    at resolvePromise (zone.js:1211:31)
    at zone.js:1118:17
    at zone.js:1134:33

Este snippet sofre de dois problemas:

  • Há apenas uma linha correspondente ao código que o desenvolvedor criou. Todo o resto vem de dependências de terceiros (framework Angular, Zone.js, RxJS)
  • Não há informações sobre qual interação do usuário causou o erro

Não há informações sobre qual interação do usuário causou o erro

A equipe do Chrome DevTools criou um mecanismo para ignorar scripts provenientes de node_modules anotando source maps por meio da CLI do Angular. Também foi criada uma API de marcação de pilha assíncrona que nos permitiu concatenar tarefas assíncronas agendadas e independentes em um único stack trace. Jia Li integrou o Zone.js com a API de marcação de pilha assíncrona, o que permitiu fornecer stack traces vinculados.

Essas duas mudanças melhoram drasticamente o stack trace que os desenvolvedores veem no Chrome DevTools:

ERROR Error: Uncaught (in promise): Error
Error
    at app.component.ts:18:11
    at fetch (async)  
    at (anonymous) (app.component.ts:4)
    at request (app.component.ts:4)
    at (anonymous) (app.component.ts:17)
    at submit (app.component.ts:15)
    at AppComponent_click_3_listener (app.component.html:4)

Aqui você pode acompanhar a execução do botão pressionado no AppComponent até o erro. Você pode ler mais sobre as melhorias aqui.

Lançamento de componentes baseados em MDC para produção

Estamos felizes em anunciar que a refatoração dos componentes de material Angular com base no Material Design Components for Web (MDC) está concluída! Essa mudança permite que o Angular se alinhe ainda mais com a especificação do Material Design, reutilize código de primitivas desenvolvidas pela equipe do Material Design e nos permite adotar o Material 3 assim que finalizado os tokens de estilo.

Para muitos dos componentes, atualizamos os estilos e a estrutura DOM e outros reescrevemos do zero. Mantivemos a maioria das APIs TypeScript e seletores de componentes/diretivas para os novos componentes idênticos à implementação antiga.

Migramos milhares de projetos do Google, o que nos permitiu facilitar o caminho da migração externa e documentar uma lista abrangente das alterações em todos os componentes.

Devido ao novo DOM e CSS, você provavelmente descobrirá que alguns estilos em seu aplicativo precisam ser ajustados, principalmente se seu CSS estiver substituindo estilos em elementos internos em qualquer um dos componentes migrados.

A implementação antiga de cada novo componente agora está obsoleta, mas ainda está disponível a partir de uma importação “herdada”. Por exemplo, você pode importar a implementação do botão mat antigo importando o módulo de botão legado.

import {MatLegacyButtonModule} from '@angular/material/legacy-button';

Visite o guia de migração para obter mais informações.

Mudamos muitos dos componentes para usar tokens de design e variáveis CSS internamente, o que fornecerá um caminho mais tranquilo para os aplicativos adotarem os estilos de componentes do Material 3.

Mais melhorias nos componentes

Resolvemos o 4º problema mais votado - suporte à seleção de intervalo no controle slider.

Para obter uma entrada de intervalo, use:

<mat-slider>
  <input matSliderStartThumb>
  <input matSliderEndThumb>
</mat-slider>

Além disso, todos os componentes agora têm uma API para personalizar a densidade que resolveu outro problema popular do GitHub.

Agora você pode especificar a densidade padrão em todos os seus componentes personalizando seu tema:

@use '@angular/material' as mat;

$theme: mat.define-light-theme((
  color: (
    primary: mat.define-palette(mat.$red-palette),
    accent: mat.define-palette(mat.$blue-palette),
  ),
  typography: mat.define-typography-config(),
  density: -2,
));

@include mat.all-component-themes($theme);

As novas versões dos componentes incluem uma ampla gama de melhorias de acessibilidade, incluindo melhores taxas de contraste, tamanhos de alvo de toque aumentados e semântica ARIA refinada.

CDK Listbox

O Component Dev Kit (CDK) oferece um conjunto de primitivas de comportamento para a construção de componentes de interface do usuário. Na v15, introduzimos outra primitiva que você pode personalizar para seu caso de uso — a listbox CDK:

image.png

O módulo @angular/cdk/listbox fornece diretivas para ajudar a criar interações de caixa de listagem personalizadas com base no padrão de caixa de listagem WAI ARIA.

Ao usar @angular/cdk/listbox, você obtém todos os comportamentos esperados para uma experiência acessível, incluindo suporte a layout bidi, interação com teclado e gerenciamento de foco. Todas as diretivas aplicam suas funções ARIA associadas ao elemento host.

Melhorias no suporte de esbuild experimental

image.png

Na v14, anunciamos o suporte experimental para esbuild em ng build para permitir tempos de compilação mais rápidos e simplificar nosso pipeline.

Na v15, agora temos Sass experimental, modelo SVG, substituição de arquivo e ng build --watchsupport ! Por favor, tente esbuild atualizando os builder angular.json de:

"builder": "@angular-devkit/build-angular:browser"

para

"builder": "@angular-devkit/build-angular:browser-esbuild"

Se você encontrar algum problema com suas compilações de produção, informe-nos registrando um problema no GitHub.

Importações automáticas no language service

O language service agora pode importar automaticamente os componentes que você está usando em um template, mas não adicionou a um componente independente ou a um NgModule.

image.png

Melhorias na CLI

Na CLI Angular, introduzimos suporte para APIs estáveis independentes. Agora você pode gerar um novo componente independente via ng g component --standalone.

Também temos a missão de simplificar a saída de ng new. Como primeiro passo, reduzimos a configuração removendo test.ts, polyfills.ts e ambientes. Agora você pode especificar seus polyfills diretamente em angular.json na seção polyfills:

"polyfills": [
  "zone.js"
]

Para reduzir ainda mais os arquivos de configuração, agora usamos .browserlist para permitir que você defina a versão de destino do ECMAScript.

Destaques da contribuição da comunidade

Estamos gratos em compartilhar que, desde o lançamento da v14, recebemos contribuições de mais de 210 pessoas em framework, componentes e CLI! Nesta seção, gostaria de destacar dois deles.

Fornecida a capacidade de configurar opções padrão para DatePipe

Esse recurso permite que você altere globalmente a configuração de formatação padrão para DatePipe. Aqui está um exemplo com a nova API bootstrapApplication:

bootstrapApplication(AppComponent, {
  providers: [
    {
      provide: DATE_PIPE_DEFAULT_OPTIONS,
      useValue: { dateFormat: 'shortDate' }
    }
  ]
});

A configuração acima habilitará o formato shortDate para todos os locais em que você usa DatePipe em seu aplicativo.

Adicionada a tag de pré-carregamento para imagens prioritárias durante o SSR

Para garantir que as imagens prioritárias sejam carregadas o mais rápido possível, Jay Bell adicionou uma funcionalidade à diretiva de imagem para incluir uma tag <link rel="preload"> para elas ao usar o Angular Universal.

Não há necessidade de ação do seu lado se você já ativou a diretiva de imagem. Se você especificou uma imagem como prioridade, a diretiva irá pré-carregá-la automaticamente.

Descontinuações

Os lançamentos major nos permitem evoluir a estrutura em direção à simplicidade, melhor experiência do desenvolvedor e alinhamento com a plataforma da web.

Depois de analisar milhares de projetos no Google, encontramos alguns padrões raramente usados que, na maioria dos casos, são mal utilizados. Como resultado, estamos descontinuando o providedIn: 'any' é uma opção que tem uso muito limitado, além de um punhado de casos esotéricos internos ao framework.

Também estamos descontinuando o providedIn: NgModule . Ele não tem amplo uso e, na maioria dos casos, é usado incorretamente, em circunstâncias em que você deve preferir providedIn: 'root'. Se você deve realmente definir o escopo de provedores para um NgModule específico, use NgModule.providers.

Com o layout em evolução em CSS, a equipe deixará de publicar novos lançamentos de @angular/flex-layout. Continuaremos fornecendo correções de segurança e compatibilidade de navegador para o próximo ano. Você pode aprender mais sobre isso na primeira postagem do blog da nossa série “CSS moderno”.

Animado com o que vem por aí!

O lançamento do Ivy em 2020 permitiu muitas melhorias em geral que você pode encontrar já sendo lançadas. O NgModules opcional é um ótimo exemplo. Ele ajuda na redução dos conceitos com os quais os iniciantes precisam lidar como parte de sua jornada de aprendizado básica e também suporta recursos avançados, como API de composição de diretivas por meio de diretivas independentes.

Em seguida, estamos abordando melhorias em nosso pipeline de renderização do lado do servidor e reatividade, trazendo melhorias de qualidade de vida em geral!

Mal posso esperar para compartilhar com vocês o que está por vir!

 
Share this