Angular Elements permet d'encapsuler vos composants Angular en Web Components standards, offrant une vraie solution pour réutiliser du code au-delà des frontières d'un framework. Contrairement aux approches précédentes basées sur des bibliothèques propriétaires, les Web Components reposent sur des APIs de plateforme stabilisées : Custom Elements, Shadow DOM et HTML Templates. Cette standardisation signifie que votre composant Angular exporté fonctionnera dans React, Vue, une application vanilla ou même un système de micro-frontends sans dépendances croisées. C'est particulièrement utile quand vous héritez d'une base de code fragmentée ou quand vous construisez une librairie destinée à des équipes utilisant des stacks différents.
Quand et pourquoi adopter Angular Elements
Les Web Components brillent dans trois contextes bien définis. D'abord, les micro-frontends distribués : vous pouvez développer, tester et déployer un composant Angular indépendamment, puis le consommer dans une shell application écrite en Vue ou en jQuery sans partager l'instance d'Angular. Ensuite, la composition d'équipes : une équipe backend-lourde peut maintenir des composants UI génériques (boutons, modales, datepickers) qu'une équipe frontend frontale intègre sans connaître Angular. Enfin, l'évolution progressive : migrer d'une vieille application Backbone vers Angular n'implique plus un big bang, mais une adoption graduelle où certains écrans restent en Backbone pendant que d'autres deviennent des Web Components Angular.
La décision de recourir à Angular Elements doit tenir compte du coût d'empaquetage. Un Web Component Angular Elements inclut le runtime Angular complet, ce qui signifie un bundle de 150-200 KB minifiés pour un seul composant. Si vous en importez dix dans une même page, vous dupliquez le runtime dix fois, sauf à utiliser une stratégie de bundling avancée. Pour des composants simples et autonomes (un sélecteur de date, un widget d'authentification), cette surcharge est acceptable. Pour un tableau complexe avec état partagé, mieux vaut conserver une architecture unifiée.
Mise en place concrète : du composant à l'élément personnalisé
Commencez par un composant Angular standard, sans dépendances externes complexes. Les formulaires réactifs, l'HTTP et les signaux fonctionnent parfaitement dans un Web Component. Installez @angular/elements via npm, puis déclarez votre composant comme entryComponent dans votre module (ou directement en standalone si vous êtes en Angular 14+). L'étape clé consiste à créer un NgCustomElement et l'enregistrer auprès du DOM via customElements.define(). Angular Elements gère automatiquement le cycle de vie du composant, l'injection de dépendances et la détection de changements.
Voici un exemple minimaliste. Supposez un composant CounterComponent qui gère un compteur avec deux boutons :
import { Component, Input, Output, EventEmitter } from '@angular/core';
@Component({
selector: 'app-counter',
template: `<button (click)="decrement()">−</button>
<span>{{ count }}</span>
<button (click)="increment()">+</button>`,
standalone: true
})
export class CounterComponent {
@Input() initialValue = 0;
@Output() countChanged = new EventEmitter<number>();
count = this.initialValue;
increment() { this.count++; this.countChanged.emit(this.count); }
decrement() { this.count--; this.countChanged.emit(this.count); }
}Pour exporter ce composant en Web Component, créez un fichier d'entrée (par exemple main.web-component.ts) :
import { createCustomElement } from '@angular/elements';
import { createApplication } from '@angular/platform-browser';
import { CounterComponent } from './counter.component';
(async () => {
const app = await createApplication({ providers: [] });
const counterElement = createCustomElement(CounterComponent, { injector: app.injector });
customElements.define('app-counter', counterElement);
})();Configurez ensuite votre build pour produire un bundle unique et optimisé. Le CLI Angular propose un schéma dédié (ng build --configuration production) qui gère la fusion des polyfills et l'extraction du style encapsulé. Vous obtenez un fichier JavaScript unique, prêt à être injecté dans n'importe quelle page HTML ou framework.
Interopérabilité et pièges courants
La plus grande difficulté réside dans la gestion de l'état et de la communication entre Web Components. Les attributs HTML ne peuvent transporter que des chaînes de caractères. Pour passer des objets complexes, vous devez utiliser des propriétés JavaScript (element.data = myObject) ou des événements personnalisés. Angular Elements traduit automatiquement vos @Input et @Output en propriétés et événements du DOM, mais cette traduction peut causer des surprises si vous attendez une réactivité synchrone.
Un piège classique : les développeurs supposent que modifier une propriété d'un Web Component met à jour le rendu instantanément. En réalité, Angular Elements utilise le mécanisme de détection de changements d'Angular, qui est asynchrone. Si vous faites element.count = 5 puis immédiatement consultez le DOM, vous risquez d'obtenir l'ancienne valeur. La solution est de vérifier la documentation de votre composant, de préférer les événements pour les mises à jour critiques ou de laisser le Web Component gérer son état en interne via des méthodes publiques.
Autre écueil : la gestion des styles. Shadow DOM encapsule le CSS, ce qui signifie que vos feuilles de style globales ne traversent pas la barrière du Web Component. Si vous utilisez Tailwind ou Bootstrap, les classes appliquées à l'extérieur ne affecteront pas le contenu interne. La solution la plus robuste est d'émettre les styles directement dans le composant Angular (via ViewEncapsulation.ShadowDom ou des feuilles de style inline) ou d'utiliser des CSS custom properties que l'hôte peut contrôler.
Optimisation et stratégies de déploiement
Pour réduire la taille du bundle, exploitez la compilation AOT et l'élimination du code mort. Angular Elements supporte tree-shaking, donc les dépendances inutilisées seront supprimées. Si vous publiez plusieurs Web Components, envisagez un registre central (par exemple, une page HTML qui charge tous les éléments) plutôt que des instances isolées. Cela vous permet de partager le runtime Angular et de diviser par dix la consommation mémoire.
Le déploiement sur un CDN est trivial : un fichier JavaScript, une ou deux feuilles de style, et c'est fait. Les navigateurs modernes supportent nativement les Web Components sans polyfill (sauf IE 11, qui est mort). Versionner vos Web Components est crucial : utilisez des suffixes dans le nom de l'élément (app-counter-v2) ou un système de versioning sémantique dans l'attribut de données, afin que les consommateurs puissent coexister avec plusieurs versions sans conflit.
Conclusion pragmatique
Angular Elements n'est pas une balle magique pour tous les problèmes d'interopérabilité, mais c'est un outil solide et standardisé pour les cas d'usage bien définis. Utilisez-les pour composer des micro-frontends, pour distribuer des composants réutilisables à travers des équipes ou pour une migration progressive. Évitez les dépendances lourdes à l'intérieur du composant, testez agressivement l'interopérabilité avec vos consommateurs cibles et mesurez l'impact en taille de bundle. La vraie force des Web Components réside dans leur indépendance vis-à-vis des frameworks : une fois exporté, votre code Angular ne demande rien d'autre que le navigateur. C'est cette neutralité qui en fait un investissement durable.