Les standalone components sont des composants Angular qui n'ont pas besoin de modules pour fonctionner. Ils importent leur propres dépendances pour fonctionner de manière "standalone".


Les standalone components sont apparus avec Angular 14 et sont maintenant utilisés par défaut dans les nouveaux projets depuis Angular 17.


Enfin, entre modules et standalone components, il n'y a plus de débat car l'équipe officielle d'Angular conseille maintenant de toujours utiliser les standalone components.

Si vous préférez le format vidéo, voici la vidéo YouTube correspondant à cet article.

Le problème des modules

Si l’équipe Angular a mis en place les composants autonomes, ce n’est pas pour rien. Ils se sont rendu compte que les modules complexifient les applications, la montée en compétences des développeurs débutants et l’expérience des développeurs.


Lorsque l'on utilisait plusieurs modules dans nos applications ou librairies Angular, cela apportait plus de complexité :

  • Augmentation du nombre de fichiers à maintenir dans notre projet avec un fichier par module
  • Génèration d'erreurs si les modules ne sont pas correctement configurés (double déclarations, composant déclaré dans le mauvais module...)
  • Nécessité de correctement configurer les modules (déclarations, imports, providers) de manière logique techniquement et fonctionnellement pour rester compréhensible
  • Nécessité de mettre à jour les modules au fur et à mesure du développement suivant l'évolution des composants.
    Par exemple, un composant qui était utilisé par un module mais qui d'un coup est utilisé par deux modules, va devoir être déplacer dans un SharedModule.

Les bénéfices des standalone components

Les standalone components apportent de nouveaux avantages qui vont simplifier notre développement :

  • Moins de friction à la création d'un composant
    Plus besoin de se poser la question “dans quel module déclarer mon composant”.
  • Montée en compétences plus simple
    Plus besoin de fichiers modules dans notre projet angular, ce qui simplifie la codebase et plus besoin de comprendre quel module importe quel module.
  • Lazy Loading plus facile
    Plus besoin de créer un module spécifique pour pouvoir mettre en place le lazy loading.

Créer un standalone component

Un composant est un standalone component lorsque la propriété standalone est à true au sein du decorator @Component().

@Component({
  selector: 'app-pokemon-card',
  standalone: true,
  templateUrl: './pokemon-card.component.html',
  styleUrl: './pokemon-card.component.scss'
})
export class PokemonCardComponent {}

Nous pouvons aussi générer un standalone component avec le ng-cli avec la commande suivante :

# Avant Angular 17, nous devons ajouter l’attribut standalone
ng generate component my-component --standalone

# Depuis Angular 17, les composants générés sont standalone par défaut
ng generate component my-component

Gérer les dépendances du standalone component

Lorsqu'un standalone component utilise des dépendances, nous devons les déclarer dans les attributs imports ou providers du @Component().


L'attribut imports permet d'importer des modules mais aussi d'autres standalone components !


Prenons deux composants, PokemonListComponent qui affichent des PokemonCardComponent. Pour fonctionner, nous devons ajouter le PokemonCardComponent dans les imports du PokemonListComponent.

@Component({
  selector: 'app-pokemon-list',
  standalone: true,
  imports: [PokemonCardComponent],
  templateUrl: './pokemon-list.component.html',
  styleUrl: './pokemon-list.component.scss'
})
export class PokemonListComponent {}

Fichier pokemon-list.component.ts

@Component({
  selector: 'app-pokemon-card',
  standalone: true,
  templateUrl: './pokemon-card.component.html',
  styleUrl: './pokemon-card.component.scss'
})
export class PokemonCardComponent {}

Fichier pokemon-card.component.ts

Si nous utilisons d'autres dépendances, nous devons aussi les importer.


Par exemple si nous utilisons la directive [routerLink] côté HTML, nous allons devoir importer RouterLink dans les imports de notre composant. Si nous utilisons les directives *ngIf ou *ngFor, nous devons importer NgIf, NgFor ou CommonModule dans notre composant.

Routing et Lazy loading

La déclaration des routes avec des standalone components est la même qu'avec des composants classiques.

export const routes: Routes = [
  {path: '', pathMatch: 'full', redirectTo: 'pokemons'},
  {path: 'pokemons', component: PokemonListComponent},
  {path: 'pokemons/:pokemonId', component: PokemonDetailsComponent}
];

L'avantage des standalone components est de pouvoir très facilement mettre en place le lazy loading. Pour cela nous pouvons utiliser l'attribut loadComponent et d'utiliser la syntaxe pour importer notre composant.

export const routes: Routes = [
  {path: '', pathMatch: 'full', redirectTo: 'pokemons'},
  {path: 'pokemons', component: PokemonListComponent},
  {
    path: 'pokemons/:pokemonId',
    loadComponent: () => import('./pokemon-details/pokemon-details.component').then(m => m.PokemonDetailsComponent)
  }
];

Bootstraper une application avec un standalone component

Si nous voulons nous débarrasser de tous les modules de notre application, nous devons utiliser un AppComponent standalone.


Pour bootstraper une application Angular avec un AppModule, nous utilisons la fonction platformBrowserDynamic().bootstrapModule(AppModule).

import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';

platformBrowserDynamic().bootstrapModule(AppModule)
  .catch(err => console.error(err));

Fichier main.ts (AppModule)

Pour bootstraper une application directement avec un standalone component, nous devons utiliser la méthode bootstrapApplication(AppComponent).

import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app/app.component';

bootstrapApplication(AppComponent)
  .catch((err) => console.error(err));

Fichier main.ts (AppComponent standalone)

Pour fournir une configuration globale à notre application, nous pouvons aussi fournir le paramètre AppConfig à la méthode bootstrapApplication.

import { bootstrapApplication } from '@angular/platform-browser';
import { appConfig } from './app/app.config';
import { AppComponent } from './app/app.component';

bootstrapApplication(AppComponent, appConfig)
  .catch((err) => console.error(err));

Fichier main.ts

import {ApplicationConfig} from '@angular/core';
import {provideRouter} from '@angular/router';
import {routes} from './app.routes';

export const appConfig: ApplicationConfig = {
  providers: [provideRouter(routes)]
};

Fichier app.config.ts

Conclusion

Nous avons vu les avantages des standalone components par rapport aux modules avec Angular.


Si vous souhaitez migrer vos applications avec modules vers des standalone components, sachez que vous pouvez le faire progressivement car il est possible d'avoir des modules et des standalone components dans la même application.

Vous pouvez accéder au code présenté au sein d'un projet Angular ici : https://github.com/GaetanRouzies/angular-standalone-vs-module

Abonnez vous pour ne pas rater les nouveaux articles !

© Gaëtan Rouziès - Tous Droits Réservés