Introducció

NgRx Effects és una biblioteca que permet gestionar efectes secundaris en aplicacions Angular utilitzant el patró Redux. Els efectes són operacions que es desencadenen en resposta a accions i poden incloure operacions asíncrones com sol·licituds HTTP, accés a l'emmagatzematge local, navegació, etc. NgRx Effects ajuda a mantenir la lògica d'efectes secundaris fora dels reducers, mantenint-los purs i fàcils de provar.

Objectius

  • Comprendre què són els efectes en NgRx.
  • Aprendre a crear i gestionar efectes.
  • Integrar efectes amb el flux d'accions i reducers.
  • Veure exemples pràctics d'ús d'efectes.

Conceptes Clau

  1. Efectes: Són classes que contenen lògica per gestionar efectes secundaris.
  2. Actions: Les accions que desencadenen els efectes.
  3. Effects Decorator: Decorador @Effect per marcar observables com efectes.
  4. Effects Module: Mòdul que registra els efectes en l'aplicació.

Creació d'Efectes

Instal·lació de NgRx Effects

Abans de començar, assegura't d'haver instal·lat NgRx Effects en el teu projecte Angular:

npm install @ngrx/effects

Configuració del Mòdul d'Efectes

Afegeix EffectsModule al mòdul principal de la teva aplicació (app.module.ts):

import { EffectsModule } from '@ngrx/effects';
import { MyEffects } from './effects/my-effects';

@NgModule({
  imports: [
    // altres imports
    EffectsModule.forRoot([MyEffects])
  ],
  // altres configuracions
})
export class AppModule { }

Creació d'una Classe d'Efectes

Crea una classe d'efectes (my-effects.ts) per gestionar els efectes secundaris:

import { Injectable } from '@angular/core';
import { Actions, ofType, createEffect } from '@ngrx/effects';
import { of } from 'rxjs';
import { catchError, map, mergeMap } from 'rxjs/operators';
import { MyService } from '../services/my-service';
import * as MyActions from '../actions/my-actions';

@Injectable()
export class MyEffects {

  constructor(
    private actions$: Actions,
    private myService: MyService
  ) {}

  loadItems$ = createEffect(() =>
    this.actions$.pipe(
      ofType(MyActions.loadItems),
      mergeMap(() => this.myService.getItems()
        .pipe(
          map(items => MyActions.loadItemsSuccess({ items })),
          catchError(error => of(MyActions.loadItemsFailure({ error })))
        ))
    )
  );
}

Explicació del Codi

  • Actions: Flux d'accions que es poden escoltar.
  • ofType: Filtra les accions per tipus.
  • createEffect: Crea un efecte.
  • mergeMap: Permet combinar múltiples observables.
  • map: Transforma les dades emeses per l'observable.
  • catchError: Gestiona errors i retorna una nova acció.

Exemple Pràctic

Suposem que tenim un servei que obté una llista d'articles des d'un servidor:

// my-service.ts
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class MyService {
  constructor(private http: HttpClient) {}

  getItems(): Observable<Item[]> {
    return this.http.get<Item[]>('https://api.example.com/items');
  }
}

I les accions corresponents:

// my-actions.ts
import { createAction, props } from '@ngrx/store';

export const loadItems = createAction('[Items] Load Items');
export const loadItemsSuccess = createAction('[Items] Load Items Success', props<{ items: Item[] }>());
export const loadItemsFailure = createAction('[Items] Load Items Failure', props<{ error: any }>());

Exercici Pràctic

Objectiu

Crear un efecte que carregui una llista d'usuaris des d'un servei i gestioni els casos d'èxit i error.

Passos

  1. Crear el servei (user.service.ts):
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { User } from '../models/user.model';

@Injectable({
  providedIn: 'root'
})
export class UserService {
  constructor(private http: HttpClient) {}

  getUsers(): Observable<User[]> {
    return this.http.get<User[]>('https://api.example.com/users');
  }
}
  1. Definir les accions (user.actions.ts):
import { createAction, props } from '@ngrx/store';
import { User } from '../models/user.model';

export const loadUsers = createAction('[User] Load Users');
export const loadUsersSuccess = createAction('[User] Load Users Success', props<{ users: User[] }>());
export const loadUsersFailure = createAction('[User] Load Users Failure', props<{ error: any }>());
  1. Crear l'efecte (user.effects.ts):
import { Injectable } from '@angular/core';
import { Actions, ofType, createEffect } from '@ngrx/effects';
import { of } from 'rxjs';
import { catchError, map, mergeMap } from 'rxjs/operators';
import { UserService } from '../services/user.service';
import * as UserActions from '../actions/user.actions';

@Injectable()
export class UserEffects {

  constructor(
    private actions$: Actions,
    private userService: UserService
  ) {}

  loadUsers$ = createEffect(() =>
    this.actions$.pipe(
      ofType(UserActions.loadUsers),
      mergeMap(() => this.userService.getUsers()
        .pipe(
          map(users => UserActions.loadUsersSuccess({ users })),
          catchError(error => of(UserActions.loadUsersFailure({ error })))
        ))
    )
  );
}
  1. Registrar l'efecte (app.module.ts):
import { EffectsModule } from '@ngrx/effects';
import { UserEffects } from './effects/user.effects';

@NgModule({
  imports: [
    // altres imports
    EffectsModule.forRoot([UserEffects])
  ],
  // altres configuracions
})
export class AppModule { }

Solució

// user.service.ts
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { User } from '../models/user.model';

@Injectable({
  providedIn: 'root'
})
export class UserService {
  constructor(private http: HttpClient) {}

  getUsers(): Observable<User[]> {
    return this.http.get<User[]>('https://api.example.com/users');
  }
}

// user.actions.ts
import { createAction, props } from '@ngrx/store';
import { User } from '../models/user.model';

export const loadUsers = createAction('[User] Load Users');
export const loadUsersSuccess = createAction('[User] Load Users Success', props<{ users: User[] }>());
export const loadUsersFailure = createAction('[User] Load Users Failure', props<{ error: any }>());

// user.effects.ts
import { Injectable } from '@angular/core';
import { Actions, ofType, createEffect } from '@ngrx/effects';
import { of } from 'rxjs';
import { catchError, map, mergeMap } from 'rxjs/operators';
import { UserService } from '../services/user.service';
import * as UserActions from '../actions/user.actions';

@Injectable()
export class UserEffects {

  constructor(
    private actions$: Actions,
    private userService: UserService
  ) {}

  loadUsers$ = createEffect(() =>
    this.actions$.pipe(
      ofType(UserActions.loadUsers),
      mergeMap(() => this.userService.getUsers()
        .pipe(
          map(users => UserActions.loadUsersSuccess({ users })),
          catchError(error => of(UserActions.loadUsersFailure({ error })))
        ))
    )
  );
}

// app.module.ts
import { EffectsModule } from '@ngrx/effects';
import { UserEffects } from './effects/user.effects';

@NgModule({
  imports: [
    // altres imports
    EffectsModule.forRoot([UserEffects])
  ],
  // altres configuracions
})
export class AppModule { }

Resum

En aquesta secció, hem après què són els efectes en NgRx i com utilitzar-los per gestionar efectes secundaris en aplicacions Angular. Hem vist com crear una classe d'efectes, definir accions i integrar els efectes amb el flux d'accions i reducers. També hem realitzat un exercici pràctic per consolidar els coneixements adquirits. Amb aquests coneixements, estàs preparat per gestionar efectes secundaris de manera eficient en les teves aplicacions Angular utilitzant NgRx Effects.

Curs d'Angular 2+

Mòdul 1: Introducció a Angular

Mòdul 2: Conceptes bàsics de TypeScript

Mòdul 3: Components i plantilles

Mòdul 4: Directives i pipes

Mòdul 5: Serveis i injecció de dependències

Mòdul 6: Enrutament i navegació

Mòdul 7: Formularis en Angular

Mòdul 8: Client HTTP i observables

Mòdul 9: Gestió d'estat

Mòdul 10: Proves en Angular

Mòdul 11: Temes avançats

Mòdul 12: Desplegament i millors pràctiques

© Copyright 2024. Tots els drets reservats