Toutes les nouveautĂ©s d'Angular 18 🚀

Toutes les nouveautĂ©s d'Angular 18 🚀

LoĂŻc Boutter (lazybobcat)
LoĂŻc Boutter (lazybobcat)

Angular 18 vient de sortir. Quelles sont les nouveautés apportées par cette version majeure ? Découvrons les !

Au sommaire :

  1. Breaking change : le compilateur d’Angular nĂ©cessite Typescript 5.4
  2. Les opĂ©rateurs de flux et defer sont dĂ©sormais “stable”
  3. Valeurs par défaut lors de la projection de contenu
  4. Route.redirectTo peut dĂ©sormais ĂȘtre une fonction
  5. Un router guard peut retourner un RedirectCommand
  6. Changement de détection sans zone.js (expérimental)
  7. Changements d’états des contrĂŽles de formulaires
  8. Dépréciation de HttpClientModule
  9. Angular.dev devient le site de documentation officiel
  10. Sources et liens

Breaking change : le compilateur d’Angular nĂ©cessite Typescript 5.4

Le “compiler” d’Angular 18 (@angular/compiler-cli) nĂ©cessite dĂ©sormais la version 5.4 de Typescript. Cela pourrait poser quelques soucis lors de la mise Ă  jour si vous avez des packages qui nĂ©cessitent une version infĂ©rieure.

Typescript Ă©tant connu pour Ă©viter Ă  tout prix les breaking changes, cela ne devrait en rĂ©alitĂ© pas poser de problĂšmes lors de l’éxĂ©cution de votre projet, mais il faudra probablement ignorer et/ou fixer quelques erreurs lors de votre npm update !

Les opĂ©rateurs de flux et defer sont dĂ©sormais “stables”

Si vous non plus vous ne pouvez plus Ă©crire un template sans @if, @for, @defer et consorts : bonne nouvelle. Ces opĂ©rateurs Ă©taient en “dĂ©veloppeur preview” avec Angular 17, leur API est dĂ©sormais “stable” avec Angular 18 !

Valeurs par défaut lors de la projection de contenu

La projection de contenu est une mĂ©canique permettant de rĂ©cupĂ©rer ce qui est placĂ© entre les balises HTML d’un composant afin de l’afficher oĂč on le souhaite dans le-dit composant.

Prenons l’exemple d’une carte PokĂ©mon. Je souhaite avoir un composant <pokemon-card>...</pokemon-card> auquel je veux fournir du contenu HTML pour afficher le nom de la carte, son coĂ»t, une image, une description et une Ă©ventuelle citation ou crĂ©dit en bas de carte. Si l’une de ces informations n’est pas fournie, il serait intĂ©ressant d’avoir un contenu affichĂ© par dĂ©faut.

Avant Angular 18

Auparavant si vous ajoutiez du contenu dans un <ng-content> vous obteniez une erreur, par exemple :

pokemon-card.component.ts
@Component({
    selector: 'pokemon-card',
    template: `<ng-content>Contenu par défaut<ng-content>`
})
export class PokemonCard {}

Vous obtenez l’erreur suivante :

✘ [ERROR] NG5002: <ng-content> element cannot have content. [plugin angular-compiler]
    src/main.ts:16:6:
      23 │       <ng-content>Contenu par dĂ©faut ?</ng-content>
         â•”       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

En rĂ©alitĂ©, il y a des façons d’y arriver, par exemple Ă  l’aide d’un @ContentChild().

À partir d’Angular 18

À partir d’Angular 18, le code ci-dessus est fonctionnel et la valeur passĂ©e dans la balise <ng-content> sera affichĂ©e si aucun contenu n’est fourni dans les balises du composant.


app.component.ts
<pokemon-card></pokemon-card>

Aucun contenu n’est fourni au composant PokemonCard, donc “Contenu par dĂ©faut” sera affichĂ©.


<pokemon-card>
    <strong>Eclair :</strong> lance une attaque Ă©lectrique sur son adversaire.
</pokemon-card>

Affichera : Eclair : lance une attaque Ă©lectrique sur son adversaire.


Et enfin


<pokemon-card>
    @if (false) {
        <em>Jamais affiché</em>
    }
</pokemon-card>


 n’affichera rien (n’affichera pas le contenu par dĂ©faut) ! Et oui car mĂȘme s’il est vide, du contenu est fourni au composant.

Route.redirectTo peut dĂ©sormais ĂȘtre une fonction

La fonction de redirection prend en paramùtre un ActivatedRouteSnapshot et doit retourner l’URL sous forme de chaüne de caractùres ou un UrlTree.

app.routes.ts
export const routes: Routes = [
    // ...,
    {
        path: 'poke-monsters', // ancienne route
        redirectTo: ({ queryParams }) => {
            const pokemonId = queryParams['id'];      
            if (pokemonId) {
                return `/pokemon/${pokemonId}`;
            } else {
                return `/pokemons`;
            }
        }
    },
    // ...,
];

Un router guard peut retourner un RedirectCommand

Afin d’effectuer une redirection dans un guard, il est maintenant possible de retourner un object de type RedirectCommand, prenant à la construction un UrlTree :

app.routes.ts
{
    path: '/old-url',
    component: SuperComponent,
    canActivate: [
        () => new RedirectCommand(router.parseUrl('/new-url'), {skipLocationChange: true}),
    ],
}

Les options, passées en second paramÚtre du constructeur, sont un objet de type NavigationBehaviorOptions.

Documentation de RedirectCommand.

Expérimental : changement de détection sans zone.js

Si vous avez mĂȘme peu d’expĂ©rience avec Angular, il y a de fortes chances que vous soyez dĂ©jĂ  tombĂ© sur une erreur incomprĂ©hensible provenant de zone.js. Pour simplifier un peu, ce vendor est chargĂ© de rapporter tous les changements et Ă©vĂ©nements d’une “zone” donnĂ©e (en gĂ©nĂ©ral, la zone est votre page web). Angular s’en servait donc pour effectuer la dĂ©tection de changement.

Avec des travaux rĂ©alisĂ©s depuis quelques annĂ©es et l’arrivĂ©e des signaux, il est dĂ©sormais possible de d’utiliser Angular sans zone.js. Pour cela, il faut appeler provideExperimentalZonelessChangeDetection() lors du dĂ©marrage de l’application (bootstrap) :

main.ts
bootstrapApplication(App, {
    providers: [
        provideExperimentalZonelessChangeDetection()
    ]
});

Vous pouvez désormais retirer zone.js du fichier angular.json. Le retrait de zone.js permet notamment :

  • d’accĂ©lĂ©rer le rendu initial de l’application
  • rĂ©duire le poids global de l’application
  • avoir plus de contrĂŽle sur la dĂ©tection de changement avec la stratĂ©gie OnPush et ChangeDetectorRef.markForCheck
  • combiner des micro-frontends plus facilement (ils ne sont plus dans des zones diffĂ©rentes)
  • Ă©viter d’avoir des erreurs incomprĂ©hensibles :)

La migration sera plus facile si vos composants ont dĂ©jĂ  la stratĂ©gie de dĂ©tection de changement OnPush. Alors si ce n’est pas le cas, je vous suggĂšre de les convertir au plus vite !

Changements d’états des contrĂŽles de formulaires

Une nouvelle propriĂ©tĂ© est disponible sur FormControl, FormGroup et FormArray : events. Il s’agit d’un observable permettant d’ĂȘtre informĂ© sur les diffĂ©rents changements d’états : valeur, touch, pristine et son Ă©tat de validation. Son utilisation est trĂšs simple :

const nameControl = new FormControl<string|null>('name', Validators.required);
nameControl.events.subscribe(event => {
    // process the individual events
});

Dépréciation de HttpClientModule

Importer HttpClientModule dans un module ou composant est désormais déprécié. Il faut utiliser la fonction provideHttpClient() dans vos providers. Par exemple :

main.ts
bootstrapApplication(App, {
    providers: [
        // ... others like provideExperimentalZonelessChangeDetection() ;)
        provideHttpClient(),
    ]
});

Si vous utilisez la commande ng update @angular/core (ou Nx) pour mettre votre projet Ă  jour, ce changement devrait ĂȘtre effectuĂ© automatiquement !

Consultez la documentation pour plus d’informations.

Angular.dev devient le site de documentation officiel

Le site, dĂ©jĂ  disponible depuis quelques mois, vient officiellement remplacer Angular.io. Ce dernier reste disponible pour l’instant (d’autant qu’il est souvent mieux rĂ©fĂ©rencĂ© sur Google) mais devrait prochainement rediriger sur la nouvelle plateforme.

Angular.dev propose de la documentation et des tutoriels interactifs (avec du live coding) ainsi qu’un playground bien pratique.

Sources et liens

Nous avons vu les changements les plus importants de cette nouvelle version, mais il y en a d’autres comme des amĂ©liorations du SSR et de l’hydratation de la page. Pour plus d’informations sur ces Ă©lĂ©ments ou un angle diffĂ©rent sur ceux Ă©voquĂ©s dans cet article, n’hĂ©sitez pas Ă  consulter les liens suivants :

Retour aux articles