NgRx Effects is a powerful library that allows you to handle side effects in your Angular applications. Side effects are operations that interact with the outside world, such as HTTP requests, logging, or interacting with browser APIs. NgRx Effects helps you isolate these side effects from your components and services, making your application more predictable and easier to test.
Key Concepts
- Effects: Functions that listen for actions dispatched from the store and perform side effects.
- Actions: Objects that represent an event in the application.
- Reducers: Functions that handle state changes based on actions.
- Store: A single source of truth for the application state.
Setting Up NgRx Effects
Step 1: Install NgRx Effects
First, you need to install the NgRx Effects library:
Step 2: Create an Effect
Create a new file for your effect, for example, auth.effects.ts
:
import { Injectable } from '@angular/core'; import { Actions, createEffect, ofType } from '@ngrx/effects'; import { AuthService } from '../services/auth.service'; import { login, loginSuccess, loginFailure } from '../actions/auth.actions'; import { catchError, map, mergeMap } from 'rxjs/operators'; import { of } from 'rxjs'; @Injectable() export class AuthEffects { constructor( private actions$: Actions, private authService: AuthService ) {} login$ = createEffect(() => this.actions$.pipe( ofType(login), mergeMap(action => this.authService.login(action.credentials).pipe( map(user => loginSuccess({ user })), catchError(error => of(loginFailure({ error }))) ) ) ) ); }
Step 3: Register the Effect
Register the effect in your AppModule
:
import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { StoreModule } from '@ngrx/store'; import { EffectsModule } from '@ngrx/effects'; import { AuthEffects } from './effects/auth.effects'; import { reducers } from './reducers'; @NgModule({ declarations: [AppComponent], imports: [ BrowserModule, StoreModule.forRoot(reducers), EffectsModule.forRoot([AuthEffects]) ], providers: [], bootstrap: [AppComponent] }) export class AppModule {}
Practical Example
Let's create a simple login effect that handles user authentication.
Step 1: Define Actions
Create a file auth.actions.ts
:
import { createAction, props } from '@ngrx/store'; export const login = createAction( '[Auth] Login', props<{ credentials: { username: string; password: string } }>() ); export const loginSuccess = createAction( '[Auth] Login Success', props<{ user: any }>() ); export const loginFailure = createAction( '[Auth] Login Failure', props<{ error: any }>() );
Step 2: Create the AuthService
Create a file auth.service.ts
:
import { Injectable } from '@angular/core'; import { Observable, of } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class AuthService { login(credentials: { username: string; password: string }): Observable<any> { // Simulate an HTTP request if (credentials.username === 'admin' && credentials.password === 'admin') { return of({ id: 1, name: 'Admin User' }); } else { throw new Error('Invalid credentials'); } } }
Step 3: Create the Effect
Create a file auth.effects.ts
:
import { Injectable } from '@angular/core'; import { Actions, createEffect, ofType } from '@ngrx/effects'; import { AuthService } from '../services/auth.service'; import { login, loginSuccess, loginFailure } from '../actions/auth.actions'; import { catchError, map, mergeMap } from 'rxjs/operators'; import { of } from 'rxjs'; @Injectable() export class AuthEffects { constructor( private actions$: Actions, private authService: AuthService ) {} login$ = createEffect(() => this.actions$.pipe( ofType(login), mergeMap(action => this.authService.login(action.credentials).pipe( map(user => loginSuccess({ user })), catchError(error => of(loginFailure({ error: error.message }))) ) ) ) ); }
Step 4: Register the Effect
Update your AppModule
:
import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { StoreModule } from '@ngrx/store'; import { EffectsModule } from '@ngrx/effects'; import { AuthEffects } from './effects/auth.effects'; import { reducers } from './reducers'; @NgModule({ declarations: [AppComponent], imports: [ BrowserModule, StoreModule.forRoot(reducers), EffectsModule.forRoot([AuthEffects]) ], providers: [], bootstrap: [AppComponent] }) export class AppModule {}
Practical Exercise
Task
- Create a new effect that handles user registration.
- Define actions for
register
,registerSuccess
, andregisterFailure
. - Implement the
register
method in theAuthService
. - Create the
RegisterEffects
class and handle the registration logic. - Register the new effect in the
AppModule
.
Solution
Step 1: Define Actions
Create a file auth.actions.ts
:
import { createAction, props } from '@ngrx/store'; export const register = createAction( '[Auth] Register', props<{ credentials: { username: string; password: string } }>() ); export const registerSuccess = createAction( '[Auth] Register Success', props<{ user: any }>() ); export const registerFailure = createAction( '[Auth] Register Failure', props<{ error: any }>() );
Step 2: Update AuthService
Update auth.service.ts
:
import { Injectable } from '@angular/core'; import { Observable, of } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class AuthService { login(credentials: { username: string; password: string }): Observable<any> { // Simulate an HTTP request if (credentials.username === 'admin' && credentials.password === 'admin') { return of({ id: 1, name: 'Admin User' }); } else { throw new Error('Invalid credentials'); } } register(credentials: { username: string; password: string }): Observable<any> { // Simulate an HTTP request return of({ id: 2, name: credentials.username }); } }
Step 3: Create the Register Effect
Create a file register.effects.ts
:
import { Injectable } from '@angular/core'; import { Actions, createEffect, ofType } from '@ngrx/effects'; import { AuthService } from '../services/auth.service'; import { register, registerSuccess, registerFailure } from '../actions/auth.actions'; import { catchError, map, mergeMap } from 'rxjs/operators'; import { of } from 'rxjs'; @Injectable() export class RegisterEffects { constructor( private actions$: Actions, private authService: AuthService ) {} register$ = createEffect(() => this.actions$.pipe( ofType(register), mergeMap(action => this.authService.register(action.credentials).pipe( map(user => registerSuccess({ user })), catchError(error => of(registerFailure({ error: error.message }))) ) ) ) ); }
Step 4: Register the Effect
Update your AppModule
:
import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { StoreModule } from '@ngrx/store'; import { EffectsModule } from '@ngrx/effects'; import { AuthEffects } from './effects/auth.effects'; import { RegisterEffects } from './effects/register.effects'; import { reducers } from './reducers'; @NgModule({ declarations: [AppComponent], imports: [ BrowserModule, StoreModule.forRoot(reducers), EffectsModule.forRoot([AuthEffects, RegisterEffects]) ], providers: [], bootstrap: [AppComponent] }) export class AppModule {}
Summary
In this section, you learned how to use NgRx Effects to handle side effects in your Angular application. You created an effect to handle user login and registration, defined actions, and registered the effects in your application module. NgRx Effects helps you manage side effects in a clean and predictable way, making your application easier to maintain and test.
Next, you will learn about state management using NgRx Store in the next module.
Angular Course
Module 1: Introduction to Angular
- What is Angular?
- Setting Up the Development Environment
- Angular Architecture
- First Angular Application
Module 2: Angular Components
- Understanding Components
- Creating Components
- Component Templates
- Component Styles
- Component Interaction
Module 3: Data Binding and Directives
- Interpolation and Property Binding
- Event Binding
- Two-Way Data Binding
- Built-in Directives
- Custom Directives
Module 4: Services and Dependency Injection
Module 5: Routing and Navigation
Module 6: Forms in Angular
Module 7: HTTP Client and Observables
- Introduction to HTTP Client
- Making HTTP Requests
- Handling HTTP Responses
- Using Observables
- Error Handling
Module 8: State Management
- Introduction to State Management
- Using Services for State Management
- NgRx Store
- NgRx Effects
- NgRx Entity
Module 9: Testing in Angular
Module 10: Advanced Angular Concepts
- Angular Universal
- Performance Optimization
- Internationalization (i18n)
- Custom Pipes
- Angular Animations