Appearance
Comprendre et Utiliser les Sélecteurs dans NgXs
Introduction:
NgXS offre une méthode déclarative pour accéder aux états grâce aux sélecteurs. Ces sélecteurs sont des fonctions qui peuvent être utilisées pour récupérer des portions de l'état. Nous allons détailler le fonctionnement des sélecteurs et comment les utiliser dans un composant Angular.
Pour suivre ce contenu, il faut déjà avoir créer l'état et l'action (voir Créer un état et Créer une action). Sinon, regardez le code en entier à la fin de ce contenu 😃
1. Qu'est-ce qu'un sélecteur ?
Un sélecteur est une méthode statique, décorée avec @Selector()
, à l'intérieur d'une classe State. Il permet de récupérer une partie ou l'ensemble des données stockées dans un état. Les sélecteurs sont optimisés pour éviter des recalculs inutiles, rendant l'accès à l'état performant.
2. Création et utilisation du sélecteur getUsersList
:
2.1. Définir le sélecteur:
Dans notre UserState
(src/app/store/users.state.ts
), nous avons le sélecteur getUsersList
:
typescript
@Selector()
static getUsersList(state: UserStateModel): User[] {
return state.usersList
}
Ce sélecteur retourne simplement la liste des utilisateurs depuis l'état.
3. Comment utiliser le sélecteur dans un composant:
3.1. Utilisez le décorateur @Select
:
NgXS fournit un décorateur pratique, @Select
, qui peut être utilisé pour injecter directement un Observable représentant une partie de l'état dans votre composant.
Dans UsersComponent
, nous utilisons ce décorateur:
typescript
users$: Observable<User[]> = this.select(UserState.getUsersList)
Cela crée un Observable users$
qui émettra la liste des utilisateurs chaque fois qu'elle est mise à jour dans l'état.
3.2. Utilisation dans le template:
Dans le fichier users.component.html
, vous pouvez utiliser users$
avec la pipe async
pour itérer sur la liste des utilisateurs:
angular-html
@for (user of users$ | async ; track user.id) {
<div class="user">
<div class="user__name">{{ user.name }}</div>
<div class="user__email">{{ user.email }}</div>
</div>
}
3.3. Charger des données:
La méthode ngOnInit
est utilisée pour déclencher l'action GetUsersAction
qui récupère les utilisateurs:
typescript
ngOnInit() {
this.store.dispatch(new GetUsersAction())
}
Code en entier
ts
import { Injectable, inject } from "@angular/core";
import { Action, State, StateContext, Selector } from "@ngxs/store";
import { Observable, tap } from "rxjs";
import { User } from "src/app/core/interfaces/user";
import { UserService } from "src/app/core/services/user.service";
import { GetUsersAction } from "./users.action";
export interface UserStateModel {
usersList: User[]
}
@State({
name: 'users',
defaults: {
usersList: []
}
})
@Injectable()
export class UserState {
private userService = inject(UserService)
@Selector()
static getUsersList(state: UserStateModel): User[] {
return state.usersList
}
@Action(GetUsersAction)
getUsers(context: StateContext<UserStateModel>, action: GetUsersAction): Observable<any> {
return this.userService.getAll(action.sort)
.pipe(
tap((users: User[]) => {
context.patchState({
usersList: users
})
})
)
}
}
ts
export class GetUsersAction {
static readonly type = '[Users] Get Users';
constructor(public sort: string) {}
}
ts
import { Component, OnInit, inject } from '@angular/core'
import { Select, Store } from '@ngxs/store';
import { Observable } from 'rxjs';
import { User } from 'src/app/core/user';
import { GetUsersAction } from 'src/app/store/users.action';
import { UserState } from 'src/app/store/users.state';
@Component({
selector: 'app-users',
templateUrl: 'users.component.html'
})
export class UsersComponent implements OnInit {
private store = inject(Store)
users$: Observable<User[]> = this.select(UserState.getUsersList)
ngOnInit() {
this.store.dispatch(new GetUsersAction())
}
}
angular-html
@for (user of users$ | async ; track user.id) {
<div class="user">
<div class="user__name">{{ user.name }}</div>
<div class="user__email">{{ user.email }}</div>
</div>
}
ts
import { HttpClient } from "@angular/common/http";
import { Injectable, inject } from "@angular/core";
import { Observable } from "rxjs";
import { User } from "./user";
@Injectable({
providedIn: "root",
})
export class UserService {
private http = inject(HttpClient);
readonly url: string = "https://jsonplaceholder.typicode.com/users";
getAll(): Observable<User[]> {
return this.http.get<User[]>(this.url);
}
}
ts
export interface User {
id: number;
name: string;
username?: string;
email: string;
address?: {
street: string;
suite: string;
city: string;
zipcode: string;
geo: {
lat: string;
lng: string;
}
};
phone?: string;
website?: string;
company?: {
name: string;
catchPhrase: string;
bs: string;
};
}