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 no afectan directamente al estado de la aplicación, como llamadas a APIs, operaciones de enrutamiento, o cualquier otra operación asíncrona. NgRx Effects permite gestionar estas operaciones de manera reactiva y declarativa utilizando Observables.

Objetivos

  • Comprender qué son los efectos en NgRx.
  • Aprender a crear y manejar efectos utilizando NgRx Effects.
  • Integrar efectos en una aplicación Angular 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. Actions: Son objetos que describen eventos que ocurren en la aplicación.
  3. Observables: Son secuencias de eventos que se pueden observar y manejar de manera reactiva.

Instalación

Antes de comenzar, asegúrate de tener NgRx Effects instalado en tu proyecto Angular. Puedes instalarlo utilizando 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 la acción loadProducts y realice una llamada a una API para cargar los productos.

// 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.getAll().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 resultados exitosos y los errores.
  2. Pasos:
    • Define las acciones loadUsers, loadUsersSuccess y loadUsersFailure.
    • Crea un servicio UserService que tenga un método getAll para obtener los usuarios.
    • Crea una clase de efectos UserEffects que escuche la acción loadUsers y realice la llamada a la API.
    • 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 el 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) {}

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

Crear la Clase de 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.getAll().pipe(
          map(users => loadUsersSuccess({ users })),
          catchError(error => of(loadUsersFailure({ error })))
        )
      )
    )
  );
}

Registrar los 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 efectos secundarios en aplicaciones Angular de manera reactiva y declarativa. Al utilizar NgRx Effects, puedes mantener tu lógica de efectos secundarios separada de tu lógica de estado, lo que resulta en un código más limpio y mantenible. En este módulo, aprendiste a crear y manejar efectos utilizando NgRx Effects, y cómo integrarlos en una aplicación Angular.

Curso de Angular 2+

Módulo 1: Introducción a Angular

Módulo 2: Conceptos Básicos de TypeScript

Módulo 3: Componentes y Plantillas

Módulo 4: Directivas y Pipes

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

Módulo 6: Enrutamiento y Navegación

Módulo 7: Formularios en Angular

Módulo 8: Cliente HTTP y Observables

Módulo 9: Gestión de Estado

Módulo 10: Pruebas en Angular

Módulo 11: Temas Avanzados

Módulo 12: Despliegue y Mejores Prácticas

© Copyright 2024. Todos los derechos reservados