Angular redux tutorials with examples

 In Angular, NgRx is the most popular library for implementing Redux-style state management. It uses RxJS and follows the principles of Redux for managing state in a predictable way with actions, reducers, and stores.

Here’s a step-by-step guide to learning how to use NgRx in an Angular application with examples.

Prerequisites:

  • Basic knowledge of Angular and TypeScript.
  • Familiarity with Redux concepts like Actions, Reducers, and Store.

Step 1: Install NgRx

To get started with NgRx, you need to install the required dependencies.

bash
ng add @ngrx/store ng add @ngrx/effects # Optional: For side effects like HTTP calls ng add @ngrx/store-devtools # Optional: For Redux DevTools integration

These commands will install @ngrx/store, @ngrx/effects, and @ngrx/store-devtools in your Angular project.

Step 2: Define the State Model

In Redux, the state is a single object that holds all the data for your application. Start by defining a state model for your application.

Example: Let’s create a simple app to manage a list of tasks.

  1. Create a file to define the state model (e.g., task.model.ts).
typescript
export interface Task { id: number; description: string; completed: boolean; } export interface TaskState { tasks: Task[]; loading: boolean; }

Step 3: Define Actions

Actions in Redux are objects that describe a state change. Each action must have a type (usually a string) and an optional payload.

Create a file for the actions (e.g., task.actions.ts).

Example:

typescript
import { createAction, props } from '@ngrx/store'; import { Task } from './task.model'; export const loadTasks = createAction('[Task List] Load Tasks'); export const loadTasksSuccess = createAction( '[Task List] Load Tasks Success', props<{ tasks: Task[] }>() ); export const loadTasksFailure = createAction( '[Task List] Load Tasks Failure', props<{ error: string }>() ); export const addTask = createAction( '[Task List] Add Task', props<{ task: Task }>() ); export const toggleTask = createAction( '[Task List] Toggle Task', props<{ id: number }>() );

Step 4: Create a Reducer

Reducers specify how the state changes in response to actions. The reducer function takes the current state and an action, then returns a new state based on the action.

Create a file for the reducer (e.g., task.reducer.ts).

Example:

typescript
import { createReducer, on } from '@ngrx/store'; import { loadTasksSuccess, loadTasksFailure, addTask, toggleTask } from './task.actions'; import { TaskState } from './task.model'; export const initialState: TaskState = { tasks: [], loading: false, }; export const taskReducer = createReducer( initialState, on(loadTasksSuccess, (state, { tasks }) => ({ ...state, tasks, loading: false, })), on(loadTasksFailure, (state, { error }) => ({ ...state, loading: false, })), on(addTask, (state, { task }) => ({ ...state, tasks: [...state.tasks, task], })), on(toggleTask, (state, { id }) => ({ ...state, tasks: state.tasks.map(task => task.id === id ? { ...task, completed: !task.completed } : task ), })) );

Step 5: Register the Reducer in the Store Module

You need to register your reducer with the store in the AppModule.

Open app.module.ts and import the store and reducer.

typescript
import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { StoreModule } from '@ngrx/store'; import { taskReducer } from './state/task.reducer'; import { AppComponent } from './app.component'; @NgModule({ declarations: [AppComponent], imports: [ BrowserModule, StoreModule.forRoot({ tasks: taskReducer }), ], providers: [], bootstrap: [AppComponent], }) export class AppModule {}

Step 6: Dispatch Actions from a Component

In your Angular component, you’ll dispatch actions to trigger state changes.

Example:

In task.component.ts, dispatch actions like adding a task or toggling completion.

typescript
import { Component, OnInit } from '@angular/core'; import { Store } from '@ngrx/store'; import { Observable } from 'rxjs'; import { Task } from './state/task.model'; import { loadTasks, addTask, toggleTask } from './state/task.actions'; @Component({ selector: 'app-task', templateUrl: './task.component.html', styleUrls: ['./task.component.css'] }) export class TaskComponent implements OnInit { tasks$: Observable<Task[]>; constructor(private store: Store<{ tasks: Task[] }>) { this.tasks$ = store.select('tasks'); } ngOnInit() { this.store.dispatch(loadTasks()); } addTask(description: string) { const newTask: Task = { id: Date.now(), description, completed: false }; this.store.dispatch(addTask({ task: newTask })); } toggleTask(id: number) { this.store.dispatch(toggleTask({ id })); } }

Step 7: Display the Data in the Template

Now, display the tasks in the template (task.component.html).

html
<div *ngFor="let task of tasks$ | async"> <span [class.completed]="task.completed">{{ task.description }}</span> <button (click)="toggleTask(task.id)">Toggle Complete</button> </div> <input #taskDescription type="text" placeholder="New Task"> <button (click)="addTask(taskDescription.value)">Add Task</button>

Step 8: Add CSS to Style Tasks

You can add basic CSS for task completion in task.component.css.

css
.completed { text-decoration: line-through; }

Step 9: Handling Side Effects with NgRx Effects (Optional)

NgRx Effects are used to manage side effects, like making HTTP requests. If you want to load tasks from an API, you would use Effects.

Example:

In task.effects.ts:

typescript
import { Injectable } from '@angular/core'; import { Actions, ofType } from '@ngrx/effects'; import { Store } from '@ngrx/store'; import { Observable, of } from 'rxjs'; import { catchError, map, mergeMap } from 'rxjs/operators'; import { HttpClient } from '@angular/common/http'; import { loadTasks, loadTasksSuccess, loadTasksFailure } from './task.actions'; @Injectable() export class TaskEffects { constructor(private actions$: Actions, private http: HttpClient) {} loadTasks$ = createEffect(() => this.actions$.pipe( ofType(loadTasks), mergeMap(() => this.http.get<Task[]>('https://api.example.com/tasks').pipe( map(tasks => loadTasksSuccess({ tasks })), catchError(error => of(loadTasksFailure({ error: error.message }))) ) ) ) ); }

Then, register the effects in app.module.ts:

typescript
import { EffectsModule } from '@ngrx/effects'; import { TaskEffects } from './state/task.effects'; @NgModule({ imports: [ BrowserModule, StoreModule.forRoot({ tasks: taskReducer }), EffectsModule.forRoot([TaskEffects]), ], }) export class AppModule {}

Conclusion

You now have a basic NgRx setup in your Angular app to manage state in a Redux-style pattern! This pattern is especially useful in large applications, as it provides a clear separation between the UI and the business logic (state management).

To extend this setup:

  • You can add more actions (e.g., deleting tasks).
  • Handle asynchronous operations (e.g., using NgRx Effects for HTTP requests).
  • Integrate NgRx DevTools to debug and visualize your state changes.

This is just the starting point. NgRx offers a lot of advanced features, such as Selectors, Entity Management, and more. For more information, you can refer to the NgRx documentation.

Comments

Popular posts from this blog

Top 200 telagu words in hindi daily life?

Building strong foundational knowledge in frontend development topics

Docker and Kubernetes Tutorials and QnA