Une nouvelle version d'Angular est sortie la semaine dernière (le 2 juin 2022), le moment pour moi de faire un récap des gros changements depuis la version 13.
Strictly Typed Reactive Forms
Cette amélioration était attendue depuis très longtemps : on a maintenant des Reactive Forms parfaitement typé. Il n'y a plus de any
implicite qui vont traîner et nous créer des erreurs qu'on ne devrait pas avoir en TypeScript.
En premier lieu : aucun stress vous n'aurez pas à repasser sur tout votre code au moment de la migration. Le CLI va automatiquement remplacer les usages de FormGroup
, FormControl
, FormArray
et FormBuilder
par respectivement UntypedFormGroup
, UntypedFormControl
, UntypedFormArray
et UntypedFormBuilder
, qui sont des alias du premier type mais avec any
comme type générique (exemple : UntypedFormGroup
est un alias de FormGroup<any>
).
Une fois la migration finie, vous pourrez dans un second temps revenir sur vos formulaires pour remplacer les Untyped*
pour rendre typé vos formulaires. Attention à prendre en compte que par défaut, on aura une inférence de type qui sera fait en autorisant null
comme valeur pour tous les champs, ce qui est obligatoire pour conserver la possibilité d'appeler la méthode reset()
sans paramètre comme on l'a un peu toujours fait.
Je vois laisse aller regarder l'article de Nicolas ou celui de la Ninja Squad sur le sujet pour voir comment gérer cette nullité et interdire les null
. 😉
Standalone Components
Le début de la solution à un élément d'Angular qui embête beaucoup les débutants, fait de gros débat chez les gens qui comprennent un peu plus : les NgModule
. Les Standalone Components visent justement à permettre de se débarrasser des NgModule
.
L'idée générale c'est de ne plus déclarer les imports/exports au niveau des modules mais de faire les imports directement au niveau de la directive @Component
, de cette manière chaque composant est implicitement déclaré dans son propre module mais sans syntaxe superflue (c'est totalement équivalent aux SCAM (Single Component Angular Module) mais sans avoir à le coder manuellement).
Pour donner une idée du gain, je vous montre un exemple de composant App et du bootstrap avec la nouvelle syntaxe :
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { bootstrapApplication } from '@angular/platform-browser';
import { MatCardModule } from '@angular/material/card';
import { ImageComponent } from './app/image.component';
import { HighlightDirective } from './app/highlight.directive';
@Component({
selector: 'app-root',
standalone: true, // ajout de cette propriété
imports: [
// import de standalone Components, Directives and Pipes
ImageComponent, HighlightDirective,
// et de comme avant NgModules
CommonModule, MatCardModule
],
template: `
<mat-card *ngIf="url">
<app-image-component [url]="url"></app-image-component>
<h2 app-highlight>{{name | titlecase}}</h2>
</mat-card>
`
})
export class AppComponent {
name = "emma";
url = "www.emma.org/image";
}
// Cette appel remplace le contenu du app.module.ts
bootstrapApplication(AppComponent);
Il faut noter qu'on peut aussi rendre standalone un Pipe
ou une Directive
avec la même logique que pour les Component
.
À noter que les Standalone Components sont considérés comme en preview sur la version 14 d'Angular, donc c'est sans doute un peu tôt pour réécrire les modules de vos applications en Standalone Components, mais c'est parfaitement opérationnel pour des petits projets ou des projets persos.
Comme pour le point précédent je vous renvoie à l'article de Nicolas ou celui de la Ninja Squad pour plus de détail sur les Standalone Components avec entre autres les implications sur le lazy loading des Standalone Components via le router et quelques notes sur les providers. 😉
La nouvelle fonction inject()
Une nouvelle fonction a été introduite dans Angular 14 pour permettre de récupérer une référence gérée par l'injecteur de dépendance d'Angular pour peu qu'on l'appelle au moment de l'initialisation d'une classe. L'intérêt est très clair : permettre de la réutilisation de code très simplement :
// params.helper.ts
export function getParam<T>(key: string): Observable<T> {
const route = inject(ActivatedRoute);
return route.paramMap.pipe(
map(params => params.get(key)),
distinctUntilChanged()
);
}
// todo-details.component.ts
@Component({ ... })
export class TodoComponent {
todoId$ = getParam<Todo>('id');
todo$ = todoId$.pipe(
switchMap(id => inject(TodoService).getTodo(id))
);
}
Je sais pas vous, mais personnellement je trouve que ça a un petit air des hooks qu'on trouve dans React et Vue 3, bientôt des composants fonctions ? 😎
Je vais me répéter un peu, mais je vous conseille à nouveau l'article de Nicolas sur le sujet si vous voulez plus de détails ! 😉
Quelques petites nouveautés
Définir le titre de la page au routage
Petite nouveauté bien sympa : on peut maintenant définir le titre de la page (le contenu de la balise <title>
dans le <head>
et donc le nom de l'onglet) dès le routeur.
Pour un titre statique on pourra donc écrire quelque chose comme ça :
export const routes: Routes = [
{
path: 'todos',
title: 'Todos',
loadChildren: () => import('./todos-page/todos.routes')
.then(m => m.todosRoutes)
}
]
Comme l'équipe Angular sait très bien qu'on a parfois besoin d'un titre totalement dynamique, on pourra remplacer la chaîne de caractère par la référence vers un résolveur du type :
@Injectable({ providedIn: 'root' })
class TodoTitle implements Resolve<string> {
constructor(private todosRepo: TodosRepository) { }
resolve(route: ActivatedRouteSnapshot) {
return this.todosRepo.active$.pipe(
map(todo => todo.title),
take(1)
);
}
}
Avec une syntaxe très proche on pourra aussi définir une stratégie (une classe qui étend TitleStrategy
) pour définir le titre des pages via une fonction unique à chaque changement de route (potentiellement plus pratique si on a des routes très dynamiques pour s'éviter beaucoup de déclarations).
Je vous laisse regarder l'article de Netanel Basal sur le sujet pour avoir plus de détail sur les titres dynamiques et stratégies de titre.
Guide pour le change detection
Ce n'est pas à proprement parler d'un ajout au framework mais juste un travail de fond autour de la documentation d'Angular qui se matérialise par exemple par ce nouveau guide pour utiliser correctement les stratégies de change detection en Angular.
Le guide se trouve ici : https://angular.io/guide/change-detection.
Meilleure aide pour les templates
Outre de nouveaux outils interne au CLI pour avoir plus d'aide pour code mieux le code de nos templates, cette version ajoute aussi une erreur précise si on inverse les parenthèses et crochet des banana-box (si on écrit ([])
au lieu de [()]
). C'est un petit détail mais ça peut permettre de gagner beaucoup de temps si on ne voit pas l'erreur de suite !
On aura aussi une nouvelle erreur pour indiquer qu'on utilise un nullish coalescing operator sur une valeur non nullable.
Comme souvent avec ce genre d'ajout, on pourra configurer dans le tsconfig si on veut ignorer ces erreurs (je vous le déconseille) ou en faire de simple warning (potentiellement raisonnable si on a un gros projet).
Tree-shakeable errors
Pour optimiser encore mieux le bundle de production, mais garder des messages d'erreurs utilisables pour corriger les erreurs une fois compilé pour la production, une nouvelle idée a été mis en place.
Tous les messages d'erreurs sont maintenant tree-shakeable et seront donc supprimé à la compilation pour la production. Mais en parallèle tous les messages d'erreurs auront maintenant un code d'erreur avec une nomenclature précise. C'est uniquement ce code qui sera conservé en production et on pourra retrouver le sens du code via la page dédiée de la documentation : https://angular.io/errors.
Quelques ajouts au CLI
Un build beaucoup plus rapide en option !
C'est encore en expérimental côté Angular CLI mais on peut maintenant configuré le le build pour utilisé esbuild
à la place de webpack
comme actuellement par défaut. esbuild
est un projet populaire et qui gagne de jour en jour en popularité grâce à ses très bonnes performances. Je pense que si les retours sont positifs, esbuild
finira par remplacer webpack
donc si vous avez des petits projets, n'hésitez pas à tester !
ng e2e
, lint
et deploy
Alors qu'en version 12 l'équipe Angular faisait disparaitre l'implémentation par défaut de ng lint
et ng e2e
, cette version nous ramène ces commandes avec la possibilité de choisir une implémentation.
Pour le lint, on aura que eslint
comme choix. Pour les e2e on aura le choix entre Cypress, Nightwatch et WebdriverIO. Pour le deploy on aura la possibilité de choisir Amazon S3, Azure, Firebase, Netlify, NPM ou Github Pages. Bien sûr on pourra toujours choisir une implémentation personnalisé pour lancer la commande qu'on souhaite.
Pour moi c'est une plutôt bonne nouvelle !
Devtools !
Pour moi la nouvelle la plus importante du côté des Devtools est qu'ils sont enfin totalement compatibles avec Firefox et que les Angular Devtools sont disponibles sur le store d'addons de Mozilla : https://addons.mozilla.org/en-US/firefox/addon/angular-devtools/ ! 🎉🎉🎉
En parallèle un petit travail de fond a été fait pour rendre le devtool 100% fonctionnel même sans connexion Internet.
Conclusion
Même si on dit souvent qu'Angular bouge très peu, en réalité beaucoup de chose se passe au niveau du framework. Récemment les versions étaient petites, mais on voit la conclusion de travaux qui durent depuis plusieurs mois/années ici. Avec des changements bienvenus et potentiellement révolutionnaires !
Je n'ai résumé ici que les changements les plus importants à mon avis, je vous renvoie donc aux sources que j'ai utilisé qui évoque bien d'autres changements !
Sources :
-
Angular 14 et Angular CLI 14 :
- https://github.com/angular/angular/releases/tag/14.0.0
- https://blog.angular.io/angular-v14-is-now-available-391a6db736af
- https://blog.ninja-squad.com/2022/06/02/angular-cli-14.0/
- https://blog.ninja-squad.com/2022/06/02/what-is-new-angular-14.0/
- https://netbasal.com/whats-new-in-angular-v14-df1b0c5d5e2f
-
Strictly Typed Reactive Forms :
-
Angular Standalone Components :
-
Amélioration de l'injection de dépendance :
- https://dev.to/nicoss54/composons-avec-linjection-3jga
- https://netbasal.com/getting-to-know-the-environment-initializer-injection-token-in-angular-9622cb824f57
- https://netbasal.com/unleash-the-power-of-di-functions-in-angular-2eb9f2697d66
- https://netbasal.com/new-in-angular-13-pass-an-injector-to-embedded-views-cb71155025b4
-
Autres :
Crédit photo : https://pixabay.com/photos/number-fourteen-white-brown-2213576/