Angular Signals représente une évolution majeure dans la gestion de l'état et de la réactivité au sein des applications Angular. Introduite avec Angular 16, cette fonctionnalité révolutionne la manière dont nous gérons les données et leurs mises à jour dans nos applications frontend modernes. Pour les développeurs .NET habitués aux patterns réactifs comme IObservable, les Signals offrent une approche familière tout en apportant des optimisations spécifiques au framework Angular.
Comprendre les Signals : concepts fondamentaux
Un Signal est une wrapper autour d'une valeur qui permet de suivre et de réagir à ses changements de manière performante. Contrairement aux Observables de RxJS qui peuvent émettre plusieurs valeurs au fil du temps, un Signal représente toujours une valeur unique qui peut être mise à jour.
// Création d'un Signal basique
const count = signal(0);
// Lecture de la valeur
console.log(count()); // 0
// Mise à jour de la valeur
count.set(1);
// Mise à jour basée sur la valeur précédente
count.update(value => value + 1);
Architecture et intégration avec Angular
Les Signals s'intègrent naturellement dans l'architecture d'Angular et peuvent remplacer avantageusement certains usages des Observables et du Change Detection classique.
@Component({
selector: 'app-counter',
template: `
Count: {{ count() }}
`
})
export class CounterComponent {
count = signal(0);
increment() {
this.count.update(value => value + 1);
}
}
Computed Signals et réactivité
Les Computed Signals permettent de dériver automatiquement des valeurs à partir d'autres Signals. C'est similaire aux propriétés calculées de Vue.js ou aux selectors de NgRx.
const count = signal(0);
const doubled = computed(() => count() 2);
// La valeur de doubled est automatiquement mise à jour
count.set(5);
console.log(doubled()); // 10
Effects et gestion des effets de bord
Les Effects permettent d'exécuter du code en réaction aux changements des Signals, idéal pour la gestion des effets de bord comme les appels API ou les mises à jour du DOM.
const currentUser = signal(null);
effect(() => {
if (currentUser()) {
// Exécuté à chaque changement de currentUser
saveToLocalStorage('user', currentUser());
}
});
Patterns d'utilisation avancés
Voici quelques patterns avancés pour tirer le meilleur parti des Signals :
Pattern Service avec Signals
@Injectable({
providedIn: 'root'
})
export class UserService {
private _users = signal([]);
public users = this._users.asReadonly();
async fetchUsers() {
const data = await this.http.get('/api/users').toPromise();
this._users.set(data);
}
}
Tests unitaires avec Signals
Les Signals peuvent être facilement testés grâce à leur API simple et prévisible.
describe('CounterComponent', () => {
let component: CounterComponent;
beforeEach(() => {
component = new CounterComponent();
});
it('should increment counter', () => {
expect(component.count()).toBe(0);
component.increment();
expect(component.count()).toBe(1);
});
});
Optimisation des performances
Les Signals offrent plusieurs avantages en termes de performances :
- Granularité fine des mises à jour
- Réduction des cycles de détection de changements
- Meilleures performances mémoire comparées aux Observables
Bonnes pratiques
- Préférer les Signals pour l'état local des composants
- Utiliser computed() pour les valeurs dérivées
- Éviter les effects dans les templates
- Combiner avec RxJS uniquement quand nécessaire
Migration et adoption progressive
Pour adopter les Signals dans un projet existant :
- Commencer par les nouveaux composants
- Migrer progressivement les services d'état
- Remplacer les BehaviorSubject par des Signals quand possible
Conclusion
Les Signals représentent une avancée significative pour Angular, offrant une approche plus simple et plus performante de la réactivité. Pour les développeurs .NET, c'est une opportunité d'utiliser des patterns familiers tout en bénéficiant des optimisations spécifiques à Angular. Leur adoption croissante dans l'écosystème Angular en fait une technologie clé à maîtriser pour le développement frontend moderne.
Les Signals ne remplacent pas complètement RxJS ou NgRx, mais offrent une alternative élégante pour de nombreux cas d'usage. Leur intégration native avec Angular et leur API intuitive en font un excellent choix pour la gestion d'état dans les applications Angular modernes.