Ca y est la préparation de la version 16 d’angular est lancée ! Et une grande nouveauté ce sont les Signaux ! Où comment se passer de rxjs ? Et peut-être même du detect changes, onchanges hook ?
D’où ça vient ?
Voyons d’abord d’où ça vient. C’est très inspiré de SolidJs. Où leur utilité est quasi une pierre de la librairie.
En gros, c’est comme du rxjs behavior subject, sans rxjs, et le subscribe ! Dingue non ?
Voyons comment mettre ça en place !
Décortiquons le signal
On crée signal avec : const store = signal<type>(valeur par defaut).
On next une valeur avec : store.set(valeur)
On update une valeur (projection) avec : store.update(item => item + x)
Toute comparaison avec du redux, ou un gestionnaire d’état serait .. fortuite ?! 😀
Signal, Avec un service
Créons un service
@Injectable({
providedIn: 'root'
})
export class CounterService {
count = signal(0);
init(value = 10): void {
this.count.set(value);
}
increase(value = 1): void {
this.count.update(item => item + value);
}
}
Appelons ce service dans notre composant Angular 16
export class DiscoverSignalsComponent {
private readonly service = inject(CounterService);
counter = this.service.count;
}
Puis appelons le côté template js (euh html :D)
<div>
{{ counter() }}
</div>
Noter l’appel de la fonction. (important pour la récup du signal)
Pour increase, il suffit de set, ou update :
// template
<button (click)="init()">Init</button>
<button (click)="increase()">Increase</button>
// component.ts
increase(): void {
this.service.increase();
}
// service
increase(value = 1): void {
this.count.update(item => item + value);
}
Signal, en constante
On peut même aller plus loin en se passant de service
import { effect, signal } from "@angular/core";
export const counter = signal(0);
const itemEffect = effect(() => {
console.log('MyValue changed', counter());
});
export const init = (value = 10) => {
counter.set(10);
}
export const increase = (value = 1) => {
counter.update(item => item + value);
}
Tu noteras ici, l’effect. Il permet de s’attacher à toute modification de l’état, via le set ou l’update sur le signal <3 !
Et ça nous donne côté composant :
// html
<div>
{{ counter() }}
</div>
<button (click)="init()">Init</button>
<button (click)="increase()">Increase</button>
// component.ts
export class DiscoverSignalsComponent {
// private readonly service = inject(CounterService);
// counter = this.service.count;
counter = counter;
init(): void {
// this.service.init();
init();
}
increase(): void {
// this.service.increase();
increase();
}
}
Sexy, non ?! 🙂
Signal, fetch et gestionnaire d’état
Aller on va plus loin ! Créons une sorte de mini store, et appelons une liste de planètes, depuis l’api starwars api 🙂
On crée notre store, avec de l’injection (injection token), et une fonction factory :
export function createStore() {
const store = signal<ApiPlanetsResult>({ results: [] });
const selectorAll = () => store;
const loadAll = async () => {
const response = await fetch('https://swapi.dev/api/planets');
store.set(await response.json());
}
return {
selectorAll,
loadAll
};
}
export const PLANETS_STORE = new InjectionToken<ReturnType<typeof createStore>>('Planets store with Signals');
export function provideTodosStore() {
return { provide: PLANETS_STORE, useFactory: createStore };
}
Puis on l’appelle depuis notre composant typescript 😀
@Component({
selector: 'app-list-planets',
standalone: true,
imports: [CommonModule],
templateUrl: './list-planets.component.html',
styleUrls: ['./list-planets.component.css'],
providers: [
provideTodosStore()
]
})
export class ListPlanetsComponent implements OnInit {
store = inject(PLANETS_STORE);
state = this.store.selectorAll();
ngOnInit(): void {
this.store.loadAll();
}
}
Tu noteras ici l’injection de notre token 🙂 et donc on récupère notre store
Et l’appel dans le composant est simplifié :
<select>
<option *ngFor="let item of state().results" value="{{ item.name }}">{{item.name}}</option>
</select>
Le mot de la fin
Nous avons adoré chez DevToBeCurious les Signaux.
Ca nous a amené quand même des questions :
- Quid de l’intéraction entre rxjs et signal ?
- Et les stores (genre ngrx) vont-ils aller vers là ?
- Niveau performance ? Plus rapide ?
- Promise, rxjs, signal ? tous ensemble ?
- Comment ça se passe avec le detectchanges ? Plus besoin de zone.js ?