Migrez votre application Angular vers les standalone components : abandonnez les NgModules et simplifiez votre architecture avec cette approche moderne.
Le problème des NgModules
Depuis Angular 2, les NgModules servent de conteneurs pour regrouper composants, directives, pipes et services. Si ce système est puissant, il souffre de plusieurs inconvénients :
- Boilerplate : chaque composant doit être déclaré dans un module
- Lisibilité : les dépendances d'un composant sont dans un autre fichier
- Compilation : les modules augmentent le temps de compilation
- Tree-shaking : un module entier est inclus même si on utilise un seul composant
Concept des standalone components
Un composant standalone est auto-suffisant : il déclare ses propres dépendances dans son décorateur @Component, sans avoir besoin d'un NgModule parent.
// Composant classique (nécessite un NgModule)
@Component({
selector: 'app-user-card',
template: `...`
// Dépendances déclarées dans AppModule ou UserModule
})
export class UserCardComponent {}
// Composant standalone (auto-suffisant)
@Component({
selector: 'app-user-card',
standalone: true,
imports: [CommonModule, RouterLink, DatePipe], // dépendances ici
template: `...`
})
export class UserCardComponent {}
Créer un composant standalone
Avec Angular CLI 17+, les composants sont standalone par défaut :
# Nouveau composant standalone (défaut depuis Angular 17)
ng generate component user-profile
# Forcer l'ancien style (avec module)
ng generate component user-profile --no-standalone
Un composant standalone peut importer d'autres composants standalone, des modules Angular (CommonModule, FormsModule), ou des modules tiers :
@Component({
selector: 'app-dashboard',
standalone: true,
imports: [
// Autres composants standalone
UserCardComponent,
ChartComponent,
// Modules Angular intégrés
NgIf,
NgFor,
AsyncPipe,
// Ou en bloc
CommonModule,
// Modules tiers
MatButtonModule,
ReactiveFormsModule
],
template: `
<app-user-card [user]="user" />
<app-chart [data]="chartData" />
`
})
export class DashboardComponent {}
Bootstrap d'une app standalone
Une application entièrement standalone se bootstrap sans AppModule :
// main.ts
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app/app.component';
import { appConfig } from './app/app.config';
bootstrapApplication(AppComponent, appConfig)
.catch(console.error);
// app.config.ts
import { ApplicationConfig } from '@angular/core';
import { provideRouter } from '@angular/router';
import { provideHttpClient } from '@angular/common/http';
export const appConfig: ApplicationConfig = {
providers: [
provideRouter(routes),
provideHttpClient(),
]
};
Migration automatique avec ng generate
Angular fournit un schematic de migration en 3 étapes :
# Étape 1 : Convertir les composants, directives et pipes en standalone
ng generate @angular/core:standalone
# Choisir l'option "Convert all components, directives and pipes"
# Puis choisir "Remove unnecessary NgModule classes"
# Puis choisir "Bootstrap the application using standalone APIs"
Lazy loading sans modules
Avant les standalone components, le lazy loading nécessitait un module dédié. Désormais, on charge directement un composant :
// routes.ts
import { Routes } from '@angular/router';
export const routes: Routes = [
{
path: 'dashboard',
// Ancien style : loadChildren: () => import('./dashboard/dashboard.module')
loadComponent: () => import('./dashboard/dashboard.component')
.then(m => m.DashboardComponent)
},
{
path: 'admin',
loadChildren: () => import('./admin/admin.routes')
.then(m => m.adminRoutes)
}
];
loadComponent charge un seul composant en lazy. loadChildren peut pointer vers un tableau de routes (fichier *.routes.ts) pour lazy-loader un groupe de routes sans module.