Angular 17.3: conheça a nova API output() e Host Attribute Token
Confira as principais atualizações do Framework Frontend Angular 17.3. Esta versão apresenta uma nova API de output, o HostAttributeToken, suporte ao TypeScript 5.4, entre outras melhorias.
![Angular 17.3: conheça a nova API output() e Host Attribute Token](/content/images/size/w1200/2024/04/angular-17-3-1.jpg)
Desde o lançamento da versão 17.0.0 do framework frontend Angular, no dia 8 de novembro de 2023, várias novidades foram introduzidas. Inclusive temos um post dedicado ao lançamento desta versão.
Pouco tempo depois, no dia 13 de março de 2024, foi lançada a versão 17.3.0 do Angular. Atualmente, em abril de 2024, já estamos na versão 17.3.3.
Neste texto vou destacar algumas novidades que surgiram nesta minor, 17.3.
17: Major - Indica mudanças incompatíveis com versões anteriores.
3: Minor - Indica adição de novas funcionalidades, sem quebrar a compatibilidade.
1: Patch - Indica correções de bugs, sem quebrar a compatibilidade.
Link com mais detalhes sobre semantic version.
Novidades do Angular 17.3: Suporte ao TypeScript 5.4
Nesta versão do Angular, 17.3, foi adicionado o suporte para TypeScript 5.4. Não vou entrar nos detalhes das novidades do TypeScript, mas deixo este link com a lista completa. Entretanto, vou abordar rapidamente um assunto que poderá ajudá-lo e está relacionado a versão do Angular, TypeScript e VS Code.
Definindo a versão do TypeScript no VS Code para trabalhar com versões anteriores do Angular
Em linhas gerais, no VS Code, podemos escolher entre duas versões do TypeScript:
- A versão suportada pelo próprio VS Code, que se limita ao suporte da linguagem, ou seja, você terá o syntax highlight e validação de erros na tela, porém, para realizar a transpilação do código, ainda é necessário instalar o compilador
tsc
. - A versão específica do TypeScript para o projeto em questão, encontrada no arquivo
package.json
.
tsc
. Link
Por termos 2 versões, eventualmente elas podem não ser a mesma. Isso significa que você pode estar trabalhando em um projeto com TypeScript 5.4.2, enquanto a configuração do seu VS Code permanece na versão 5.3.2. Como resultado, você pode inadvertidamente escrever um código válido na versão 5.4.2, mas inválido na 5.3.2. Isso pode levar o VS Code a destacar erros no código, mesmo que o projeto compile com sucesso. Isso ocorre porque a versão do compilador TypeScript é definida no arquivo package.json
do seu projeto, e não pela configuração global do VS Code.
No exemplo a seguir, temos um projeto na versão 17.3.0 do Angular, com suporte ao TypeScript 5.4. O VS Code está na versão 1.87.2, que oferece suporte nativo para o TypeScript 5.3.2. No entanto, mesmo com o Angular utilizando o TypeScript 5.4, ao adicionar um bloco de código válido nesta versão, o VS Code sinaliza um erro. Apesar disso, o projeto ainda é compilável com sucesso.
![Printscreen do VS code mostrando um erro de TypeScript no meio do código fonte. Abaixo o Terminal mostra que o projeto está compilando corretamente.](https://consolelog.com.br/content/images/2024/04/versao-typescript-diferente-entre-vs-code-e-workspace.png)
Para resolver esse problema, é necessário informar ao VS Code que desejamos trabalhar com a versão 5.4 do TypeScript, que é a mesma versão que o projeto Angular utiliza. Para fazer isso, basta abrir qualquer arquivo .ts e pressionar:
cmd + shift + p
(MacOS)ctrl + shift + p
(Windows)
Depois digite "typescript: select typescript versions". Aparecerá uma lista de opções, como a imagem a seguir ilustra:
![Lista de versões do TypeScript no VS Code. Neste exemplo temos as versões 5.4.3 e 5.3.2](https://consolelog.com.br/content/images/2024/04/alterando-versao-typescript-vs-code.jpg)
Basta selecionar a opção desejada. Neste exemplo, a opção desejada é a do projeto Angular, que é a 5.4.
No mundo real, você precisará fazer esta seleção quando estiver trabalhando com o VS Code atualizado e um projeto TypeScript mais antigo, por exemplo, um projeto em Angular 8 nos dias atuais.
![Printscreen do VS code mostrando código válido de TypeScript. Abaixo o Terminal mostra que o projeto está compilando corretamente.](https://consolelog.com.br/content/images/2024/04/utilizando-versao-typescript-5-4-vs-code.png)
Na tabela abaixo há uma lista de versões do Angular vs Node.js vs TypeScript vs RxJS. Retirei essa tabela desse link.
![Tabela de versões do Angular vs Node.js vs TypeScript vs RxJS](https://consolelog.com.br/content/images/2024/04/versoes-angular-typescript-nodejs-rxjs.png)
Nova API output()
Na versão 16 do Angular, foi introduzido o Signals. Inclusive comentamos por aqui: Introdução ao Angular Signals.
Desde então, deu-se início a construção de uma nova API que gira em torno do Signals. Por exemplo, quando queremos que um componente possa receber uma informação externa, utilizamos o @Input()
ou o input
, lembrando que o último está integrado ao Signals.
Nesta versão, 17.3, liberaram a API output
como developer preview, que no seu uso é similar ao @Output
. Este recurso deve ser utilizado para externalizar alguma informação do componente. Por exemplo:
import { Component, input, output } from '@angular/core';
@Component({
selector: 'app-output-test',
standalone: true,
template: `<button (click)="test()">
Button: {{ buttonLabel() }}
</button>`,
})
export class OutputTestComponent {
onClick = output<Date>();
buttonLabel = input<string>('Test');
test() {
this.onClick.emit(new Date());
}
}
output-test.component.ts
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { OutputTestComponent } from './output-test.component';
@Component({
selector: 'app-root',
standalone: true,
imports: [OutputTestComponent, CommonModule],
templateUrl: './app.component.html',
styleUrl: './app.component.css',
})
export class AppComponent {
curDate1: Date = new Date();
}
app.component.ts
<h1>Output</h1>
<app-output-test
buttonLabel="Test novo output"
(onClick)="curDate1 = $event"></app-output-test>
<div>curDate1: {{ curDate1 | date : 'longTime' }}</div>
app.component.html
![Animação mostrando que ao clicar no botão, o texto abaixo é atualizado com o valor da data corrente](https://consolelog.com.br/content/images/2024/04/angular_17_3_output_api_test.gif)
Outras vantagens que podemos destacar no uso do output():
- está em linha com o Signals, assim como os inputs, queries e model();
- a API foi simplificada, ficando somente o que é relevante para a funcionalidade. Comparando com o antigo
@Output()
onde utilizávamos oEventEmitter
, ao efetuar um.subscribe()
terá 3 possibilidades de callback: success, error e complete, já no output não, é só um callback:
onClick = output<Date>();
this.onClick.subscribe((value: Date) =>
console.log(value)
);
// ***
@Output() onClick2 = new EventEmitter<Date>();
this.onClick2.subscribe({
next: (value: Date) => console.log(value),
error: (error: any) => console.error(error),
complete: () => console.log('complete'),
});
Link da documentação oficial a respeito do novo output
.
HostAttributeToken
Com o HostAttributeToken, podemos passar um valor estático ao construir um componente, mas antes que você afirme: "ok, eu já tenho o @Input
ou input
para passar valores para o componente, o que o HostAttributeToken
faz de diferente?"
A distinção principal reside no fato de que os valores transmitidos via HostAttributeToken
não acionam a detecção de mudanças (change detection), enquanto os valores passados através de @Input()/input()
, mesmo que estáticos, ativam a detecção de mudanças. Portanto, se o valor que você deseja passar para o componente não mudará ao longo do ciclo de vida deste, o HostAttributeToken
é uma escolha apropriada.
import {
Attribute,
Component,
HostAttributeToken,
inject,
} from '@angular/core';
@Component({
selector: 'app-host-attr-test',
standalone: true,
template: `
<input [type]="inputType" [value]="inputValue" />
`,
})
export class HostAttrTestComponent {
inputType: string = '';
inputValue: string | null = inject(
new HostAttributeToken('value'),
{ optional: true }
);
constructor(@Attribute('type') type: string) {
this.inputType = type;
}
}
host-attr-test.component.ts
Quem for utilizar o componente do exemplo acima, poderá passar um valor direto no atributo do seletor:
<app-host-attr-test
type="search"
value="teste"></app-host-attr-test>
RouterTestingModule deprecation
O RouterTestingModule
foi marcado como deprecated.
Deprecated: UseprovideRouter
orRouterModule
/RouterModule.forRoot
instead. This module was previously used to provide a helpful collection of test fakes, most notably those forLocation
andLocationStrategy
. These are generally not required anymore, asMockPlatformLocation
is provided inTestBed
by default. However, you can use them directly withprovideLocationMocks
.
https://angular.io/api/router/testing/RouterTestingModule
No lugar devemos usar o RouterModule
ou provideRouter
. Veja no exemplo abaixo:
import { Routes } from '@angular/router';
import { Page1Component } from './page1/page1.component';
export const routes: Routes = [
{ path: 'page1', component: Page1Component },
{ path: 'page2', component: Page1Component },
{ path: '**', redirectTo: '/page1' },
];
app.routes.ts
import { Location } from '@angular/common';
import {
TestBed,
fakeAsync,
tick,
} from '@angular/core/testing';
import { Router, RouterModule } from '@angular/router';
import { routes } from './app.routes';
import { Page1Component } from './page1/page1.component';
import { Page2Component } from './page2/page2.component';
describe('RouterModule', () => {
let location: Location;
let router: Router;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
Page1Component,
Page2Component,
RouterModule.forRoot(routes),
],
});
router = TestBed.inject(Router);
location = TestBed.inject(Location);
router.initialNavigation();
});
it('navigate to "" redirects you to /page1', fakeAsync(() => {
router.navigate(['']);
tick();
expect(location.path()).toBe('/page1');
}));
it('navigate to "page2" takes you to /page2', fakeAsync(() => {
router.navigate(['/page2']);
tick();
expect(location.path()).toBe('/page2');
}));
});
routes.spec.ts
Considerações
O framework Angular está evoluindo constantemente. Tentei citar as novidades mais relevantes desta minor, 17.3. Abaixo há algumas referências interessantes: