Introducción

NgRx Effects es una biblioteca que se utiliza junto con NgRx Store para manejar efectos secundarios en aplicaciones Angular. Los efectos secundarios son operaciones que interactúan con el mundo exterior, como solicitudes HTTP, acceso a almacenamiento local, o cualquier otra operación asíncrona. NgRx Effects permite gestionar estas operaciones de manera reactiva y mantener la lógica de efectos secundarios fuera de los componentes y servicios.

Objetivos

  • Entender qué son los efectos en NgRx.
  • Aprender a crear y manejar efectos en una aplicación Angular.
  • Integrar NgRx Effects con NgRx Store para manejar operaciones asíncronas.

Conceptos Clave

  1. Efectos (Effects): Son clases que escuchan acciones despachadas al Store y realizan operaciones asíncronas en respuesta a esas acciones.
  2. Acciones (Actions): Son objetos que describen eventos que ocurren en la aplicación.
  3. Observables: NgRx Effects utiliza observables para manejar operaciones asíncronas.

Instalación

Antes de comenzar, asegúrate de tener NgRx Effects instalado en tu proyecto Angular. Puedes instalarlo usando npm:

npm install @ngrx/effects

Creación de Efectos

Paso 1: Definir Acciones

Primero, definimos las acciones que desencadenarán los efectos. Por ejemplo, si estamos manejando una lista de productos, podríamos tener acciones para cargar productos, cargar productos exitosamente y manejar errores.

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

export const loadProducts = createAction('[Product] Load Products');
export const loadProductsSuccess = createAction(
  '[Product] Load Products Success',
  props<{ products: Product[] }>()
);
export const loadProductsFailure = createAction(
  '[Product] Load Products Failure',
  props<{ error: any }>()
);

Paso 2: Crear el Efecto

A continuación, creamos una clase de efectos que escuche las acciones y realice las operaciones asíncronas necesarias.

// effects/product.effects.ts
import { Injectable } from '@angular/core';
import { Actions, createEffect, ofType } from '@ngrx/effects';
import { ProductService } from '../services/product.service';
import { loadProducts, loadProductsSuccess, loadProductsFailure } from '../actions/product.actions';
import { catchError, map, mergeMap } from 'rxjs/operators';
import { of } from 'rxjs';

@Injectable()
export class ProductEffects {
  constructor(
    private actions$: Actions,
    private productService: ProductService
  ) {}

  loadProducts$ = createEffect(() =>
    this.actions$.pipe(
      ofType(loadProducts),
      mergeMap(() =>
        this.productService.getProducts().pipe(
          map(products => loadProductsSuccess({ products })),
          catchError(error => of(loadProductsFailure({ error })))
        )
      )
    )
  );
}

Paso 3: Registrar los Efectos

Finalmente, registramos los efectos en el módulo de efectos de NgRx.

// app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { StoreModule } from '@ngrx/store';
import { EffectsModule } from '@ngrx/effects';
import { ProductEffects } from './effects/product.effects';
import { productReducer } from './reducers/product.reducer';

@NgModule({
  declarations: [AppComponent],
  imports: [
    BrowserModule,
    StoreModule.forRoot({ products: productReducer }),
    EffectsModule.forRoot([ProductEffects])
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule {}

Ejercicio Práctico

Ejercicio

  1. Objetivo: Crear un efecto que cargue una lista de usuarios desde una API y maneje los estados de éxito y error.
  2. Pasos:
    • Define las acciones loadUsers, loadUsersSuccess y loadUsersFailure.
    • Crea un servicio UserService que tenga un método getUsers para obtener la lista de usuarios.
    • Implementa una clase de efectos UserEffects que maneje la acción loadUsers y despache loadUsersSuccess o loadUsersFailure según corresponda.
    • Registra los efectos en el módulo de efectos de NgRx.

Solución

Definir Acciones

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

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 }>()
);

Crear Servicio

// services/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 {
  private apiUrl = 'https://api.example.com/users';

  constructor(private http: HttpClient) {}

  getUsers(): Observable<User[]> {
    return this.http.get<User[]>(this.apiUrl);
  }
}

Implementar Efectos

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

@Injectable()
export class UserEffects {
  constructor(
    private actions$: Actions,
    private userService: UserService
  ) {}

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

Registrar Efectos

// app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { StoreModule } from '@ngrx/store';
import { EffectsModule } from '@ngrx/effects';
import { UserEffects } from './effects/user.effects';
import { userReducer } from './reducers/user.reducer';

@NgModule({
  declarations: [AppComponent],
  imports: [
    BrowserModule,
    StoreModule.forRoot({ users: userReducer }),
    EffectsModule.forRoot([UserEffects])
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule {}

Conclusión

NgRx Effects es una herramienta poderosa para manejar operaciones asíncronas en aplicaciones Angular de manera reactiva. Al separar la lógica de efectos secundarios de los componentes y servicios, se mejora la mantenibilidad y la claridad del código. En este módulo, aprendiste a definir acciones, crear efectos y registrarlos en tu aplicación Angular. Con esta base, puedes manejar cualquier operación asíncrona en tu aplicación de manera eficiente y organizada.

Curso de Angular

Módulo 1: Introducción a Angular

Módulo 2: Componentes de Angular

Módulo 3: Enlace de Datos y Directivas

Módulo 4: Servicios e Inyección de Dependencias

Módulo 5: Enrutamiento y Navegación

Módulo 6: Formularios en Angular

Módulo 7: Cliente HTTP y Observables

Módulo 8: Gestión de Estado

Módulo 9: Pruebas en Angular

Módulo 10: Conceptos Avanzados de Angular

Módulo 11: Despliegue y Mejores Prácticas

© Copyright 2024. Todos los derechos reservados