{"id":25448052,"url":"https://github.com/tauhidul0821/angular-with-ngrx","last_synced_at":"2026-04-30T19:32:05.779Z","repository":{"id":276532930,"uuid":"929548432","full_name":"tauhidul0821/angular-with-ngrx","owner":"tauhidul0821","description":"Created with StackBlitz ⚡️, This project about ngrx and angular","archived":false,"fork":false,"pushed_at":"2025-03-01T23:22:56.000Z","size":79,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-06-02T00:26:07.567Z","etag":null,"topics":["angular","ngrx","state-management"],"latest_commit_sha":null,"homepage":"https://stackblitz.com/~/github.com/tauhidul0821/angular-with-ngrx","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tauhidul0821.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-02-08T19:56:53.000Z","updated_at":"2025-03-01T23:22:59.000Z","dependencies_parsed_at":"2025-03-02T00:20:42.313Z","dependency_job_id":"90b9783c-c167-4ad9-8696-e28c83f5f53c","html_url":"https://github.com/tauhidul0821/angular-with-ngrx","commit_stats":null,"previous_names":["tauhidul0821/angular-with-ngrx"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/tauhidul0821/angular-with-ngrx","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tauhidul0821%2Fangular-with-ngrx","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tauhidul0821%2Fangular-with-ngrx/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tauhidul0821%2Fangular-with-ngrx/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tauhidul0821%2Fangular-with-ngrx/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tauhidul0821","download_url":"https://codeload.github.com/tauhidul0821/angular-with-ngrx/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tauhidul0821%2Fangular-with-ngrx/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32475192,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-30T13:12:12.517Z","status":"ssl_error","status_checked_at":"2026-04-30T13:12:06.837Z","response_time":57,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["angular","ngrx","state-management"],"created_at":"2025-02-17T19:18:40.138Z","updated_at":"2026-04-30T19:32:05.765Z","avatar_url":"https://github.com/tauhidul0821.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Implementing NgRx with Angular for Users API\n\n- [youtube link](https://www.youtube.com/watch?v=zM6pUAaJZQM)\n\n## **Step 1: Install NgRx Packages**\nRun the following command in your Angular project:\n```sh\nng add @ngrx/store @ngrx/effects @ngrx/entity @ngrx/store-devtools\n```\n\n---\n\n## **Step 2: Create User Model**\nCreate a model for your user entity in `src/app/models/user.model.ts`:\n```typescript\nexport interface User {\n  id: string;\n  name: string;\n  email: string;\n}\n```\n\n---\n\n## **Step 3: Define Actions**\nCreate an actions file `src/app/store/users/users.actions.ts`:\n```typescript\nimport { createAction, props } from '@ngrx/store';\nimport { User } from '../../models/user.model';\n\n// Load Users\nexport const loadUsers = createAction('[Users] Load Users');\nexport const loadUsersSuccess = createAction(\n  '[Users] Load Users Success',\n  props\u003c{ users: User[] }\u003e()\n);\nexport const loadUsersFailure = createAction(\n  '[Users] Load Users Failure',\n  props\u003c{ error: string }\u003e()\n);\n\n// Add User\nexport const addUser = createAction('[Users] Add User', props\u003c{ user: User }\u003e());\nexport const addUserSuccess = createAction(\n  '[Users] Add User Success',\n  props\u003c{ user: User }\u003e()\n);\nexport const addUserFailure = createAction(\n  '[Users] Add User Failure',\n  props\u003c{ error: string }\u003e()\n);\n\n// Update User\nexport const updateUser = createAction(\n  '[Users] Update User',\n  props\u003c{ user: User }\u003e()\n);\nexport const updateUserSuccess = createAction(\n  '[Users] Update User Success',\n  props\u003c{ user: User }\u003e()\n);\nexport const updateUserFailure = createAction(\n  '[Users] Update User Failure',\n  props\u003c{ error: string }\u003e()\n);\n\n// Delete User\nexport const deleteUser = createAction('[Users] Delete User', props\u003c{ id: string }\u003e());\nexport const deleteUserSuccess = createAction(\n  '[Users] Delete User Success',\n  props\u003c{ id: string }\u003e()\n);\nexport const deleteUserFailure = createAction(\n  '[Users] Delete User Failure',\n  props\u003c{ error: string }\u003e()\n);\n```\n\n---\n\n## **Step 4: Create Reducer**\nCreate a reducer in `src/app/store/users/users.reducer.ts`:\n```typescript\nimport { createReducer, on } from '@ngrx/store';\nimport { User } from '../../models/user.model';\nimport * as UsersActions from './users.actions';\n\nexport interface UsersState {\n  users: User[];\n  loading: boolean;\n  error: string | null;\n}\n\nexport const initialState: UsersState = {\n  users: [],\n  loading: false,\n  error: null,\n};\n\nexport const usersReducer = createReducer(\n  initialState,\n  on(UsersActions.loadUsers, (state) =\u003e ({ ...state, loading: true })),\n  on(UsersActions.loadUsersSuccess, (state, { users }) =\u003e ({\n    ...state,\n    users,\n    loading: false,\n  })),\n  on(UsersActions.loadUsersFailure, (state, { error }) =\u003e ({\n    ...state,\n    loading: false,\n    error,\n  })),\n  on(UsersActions.addUserSuccess, (state, { user }) =\u003e ({\n    ...state,\n    users: [...state.users, user],\n  })),\n  on(UsersActions.updateUserSuccess, (state, { user }) =\u003e ({\n    ...state,\n    users: state.users.map((u) =\u003e (u.id === user.id ? user : u)),\n  })),\n  on(UsersActions.deleteUserSuccess, (state, { id }) =\u003e ({\n    ...state,\n    users: state.users.filter((user) =\u003e user.id !== id),\n  }))\n);\n```\n\n---\n\n## **Step 5: Create Effects**\nCreate effects to handle API calls in `src/app/store/users/users.effects.ts`:\n```typescript\nimport { Injectable } from '@angular/core';\nimport { Actions, createEffect, ofType } from '@ngrx/effects';\nimport { catchError, map, mergeMap, of } from 'rxjs';\nimport { UsersService } from '../../services/users.service';\nimport * as UsersActions from './users.actions';\n\n@Injectable()\nexport class UsersEffects {\n  constructor(private actions$: Actions, private usersService: UsersService) {}\n\n  loadUsers$ = createEffect(() =\u003e\n    this.actions$.pipe(\n      ofType(UsersActions.loadUsers),\n      mergeMap(() =\u003e\n        this.usersService.getUsers().pipe(\n          map((users) =\u003e UsersActions.loadUsersSuccess({ users })),\n          catchError((error) =\u003e of(UsersActions.loadUsersFailure({ error })))\n        )\n      )\n    )\n  );\n}\n```\n\n---\n\n## **Step 6: Create User Service**\nCreate `src/app/services/users.service.ts`:\n```typescript\nimport { Injectable } from '@angular/core';\nimport { HttpClient } from '@angular/common/http';\nimport { Observable } from 'rxjs';\nimport { User } from '../models/user.model';\n\n@Injectable({\n  providedIn: 'root',\n})\nexport class UsersService {\n  private apiUrl = '/api/users';\n\n  constructor(private http: HttpClient) {}\n\n  getUsers(): Observable\u003cUser[]\u003e {\n    return this.http.get\u003cUser[]\u003e(this.apiUrl);\n  }\n}\n```\n\n---\n\n## **Step 7: Register Store in App Module**\nUpdate `src/app/app.module.ts`:\n```typescript\nimport { StoreModule } from '@ngrx/store';\nimport { EffectsModule } from '@ngrx/effects';\nimport { usersReducer } from './store/users/users.reducer';\nimport { UsersEffects } from './store/users/users.effects';\n\n@NgModule({\n  imports: [\n    StoreModule.forRoot({ users: usersReducer }),\n    EffectsModule.forRoot([UsersEffects])\n  ]\n})\nexport class AppModule {}\n```\n\n---\n\n## **Step 8: Select Data in a Component**\nUse NgRx selectors in `src/app/users.component.ts`:\n```typescript\nimport { Component } from '@angular/core';\nimport { Store } from '@ngrx/store';\nimport { Observable } from 'rxjs';\nimport { User } from './models/user.model';\nimport * as UsersActions from './store/users/users.actions';\n\n@Component({\n  selector: 'app-users',\n  templateUrl: './users.component.html',\n})\nexport class UsersComponent {\n  users$: Observable\u003cUser[]\u003e = this.store.select((state) =\u003e state.users.users);\n\n  constructor(private store: Store) {\n    this.store.dispatch(UsersActions.loadUsers());\n  }\n}\n```\n\nThis completes the implementation of NgRx for CRUD operations with `/api/users`. 🚀\n\n\n\n# **Dynamically Update Header User Data using NgRx**\n\n## **1. Update User Model (`user.model.ts`)**\nEnsure the user model includes `profileImage`:\n```typescript\nexport interface User {\n  id: string;\n  name: string;\n  email: string;\n  profileImage: string;\n}\n```\n\n---\n\n## **2. Define Actions (`users.actions.ts`)**\nAdd actions for updating user profile data:\n```typescript\nimport { createAction, props } from '@ngrx/store';\nimport { User } from '../../models/user.model';\n\nexport const updateUserProfile = createAction(\n  '[User] Update Profile',\n  props\u003c{ user: Partial\u003cUser\u003e }\u003e()\n);\n\nexport const updateUserProfileSuccess = createAction(\n  '[User] Update Profile Success',\n  props\u003c{ user: User }\u003e()\n);\n\nexport const updateUserProfileFailure = createAction(\n  '[User] Update Profile Failure',\n  props\u003c{ error: string }\u003e()\n);\n```\n\n---\n\n## **3. Modify Reducer (`users.reducer.ts`)**\nHandle the user profile updates:\n```typescript\nimport { createReducer, on } from '@ngrx/store';\nimport { User } from '../../models/user.model';\nimport * as UsersActions from './users.actions';\n\nexport interface UsersState {\n  user: User | null;\n  loading: boolean;\n  error: string | null;\n}\n\nexport const initialState: UsersState = {\n  user: null,\n  loading: false,\n  error: null,\n};\n\nexport const usersReducer = createReducer(\n  initialState,\n  on(UsersActions.updateUserProfile, (state) =\u003e ({ ...state, loading: true })),\n  on(UsersActions.updateUserProfileSuccess, (state, { user }) =\u003e ({\n    ...state,\n    user,\n    loading: false,\n  })),\n  on(UsersActions.updateUserProfileFailure, (state, { error }) =\u003e ({\n    ...state,\n    loading: false,\n    error,\n  }))\n);\n```\n\n---\n\n## **4. Implement Effects (`users.effects.ts`)**\nHandle API calls for updating user profiles:\n```typescript\nimport { Injectable } from '@angular/core';\nimport { Actions, createEffect, ofType } from '@ngrx/effects';\nimport { catchError, map, mergeMap, of } from 'rxjs';\nimport { UsersService } from '../../services/users.service';\nimport * as UsersActions from './users.actions';\n\n@Injectable()\nexport class UsersEffects {\n  constructor(private actions$: Actions, private usersService: UsersService) {}\n\n  updateUserProfile$ = createEffect(() =\u003e\n    this.actions$.pipe(\n      ofType(UsersActions.updateUserProfile),\n      mergeMap(({ user }) =\u003e\n        this.usersService.updateUserProfile(user).pipe(\n          map((updatedUser) =\u003e UsersActions.updateUserProfileSuccess({ user: updatedUser })),\n          catchError((error) =\u003e of(UsersActions.updateUserProfileFailure({ error })))\n        )\n      )\n    )\n  );\n}\n```\n\n---\n\n## **5. Update User Service (`users.service.ts`)**\nModify the service to handle profile updates:\n```typescript\nimport { Injectable } from '@angular/core';\nimport { HttpClient } from '@angular/common/http';\nimport { Observable } from 'rxjs';\nimport { User } from '../models/user.model';\n\n@Injectable({\n  providedIn: 'root',\n})\nexport class UsersService {\n  private apiUrl = '/api/users';\n\n  constructor(private http: HttpClient) {}\n\n  updateUserProfile(user: Partial\u003cUser\u003e): Observable\u003cUser\u003e {\n    return this.http.put\u003cUser\u003e(`${this.apiUrl}/${user.id}`, user);\n  }\n}\n```\n\n---\n\n## **6. Dispatch Action from Profile Component**\nTrigger the update action in the Profile Component:\n```typescript\nimport { Component } from '@angular/core';\nimport { Store } from '@ngrx/store';\nimport { User } from '../../models/user.model';\nimport * as UsersActions from '../../store/users/users.actions';\n\n@Component({\n  selector: 'app-profile',\n  templateUrl: './profile.component.html',\n})\nexport class ProfileComponent {\n  constructor(private store: Store) {}\n\n  updateProfile(updatedUser: Partial\u003cUser\u003e) {\n    this.store.dispatch(UsersActions.updateUserProfile({ user: updatedUser }));\n  }\n}\n```\n\n---\n\n## **7. Select Data in Header Component**\nSubscribe to user state in the header:\n```typescript\nimport { Component } from '@angular/core';\nimport { Store } from '@ngrx/store';\nimport { Observable } from 'rxjs';\nimport { User } from '../../models/user.model';\n\n@Component({\n  selector: 'app-header',\n  templateUrl: './header.component.html',\n})\nexport class HeaderComponent {\n  user$: Observable\u003cUser | null\u003e = this.store.select((state) =\u003e state.users.user);\n\n  constructor(private store: Store) {}\n}\n```\n\n---\n\n## **8. Display User Data in Header (`header.component.html`)**\n```html\n\u003cdiv class=\"header\"\u003e\n  \u003cimg *ngIf=\"user$ | async as user\" [src]=\"user.profileImage\" alt=\"Profile Image\" /\u003e\n  \u003cspan *ngIf=\"user$ | async as user\"\u003e{{ user.name }}\u003c/span\u003e\n\u003c/div\u003e\n```\n\n\n\n\n\n\n\n---\n\n## **Final Outcome**\n- When a user updates their **name** or **profile picture** in the **Profile Component**, \n- The **Header Component** will automatically reflect the changes due to NgRx state management.\n\n🚀 **Dynamic updates without refreshing!**\n\n\n\n\nI'd be happy to teach you about Angular NgRx in detail! NgRx is a powerful state management library for Angular applications based on Redux principles. Let's break this down into manageable sections.\n\n# Comprehensive Angular NgRx Guide\n\n## Table of Contents\n1. [Introduction to NgRx](#introduction-to-ngrx)\n2. [Core Concepts](#core-concepts)\n3. [Setting Up NgRx](#setting-up-ngrx)\n4. [Actions](#actions)\n5. [Reducers](#reducers)\n6. [Selectors](#selectors)\n7. [Effects](#effects)\n8. [Store](#store)\n9. [Entity State](#entity-state)\n10. [Router Store](#router-store)\n11. [DevTools](#devtools)\n12. [Best Practices](#best-practices)\n13. [Real-World Example](#real-world-example)\n\n## Introduction to NgRx\n\nNgRx is a framework for building reactive applications in Angular. It provides state management, isolation of side effects, entity collection management, router state management, code generation, and developer tools that enhance development experience.\n\n### Why Use NgRx?\n- **Predictable State Management**: Single source of truth for application state\n- **Performance**: OnPush change detection strategy for better performance\n- **Scalability**: Scales with your application\n- **Testability**: Makes testing straightforward\n- **Developer Tools**: Time-travel debugging, state inspection\n- **Powerful Effects System**: Manage side effects like HTTP requests\n\n## Core Concepts\n\nNgRx follows the Redux pattern with Angular-specific adaptations:\n\n1. **Store**: Single source of truth for state\n2. **Actions**: Events that trigger state changes\n3. **Reducers**: Pure functions that create new state\n4. **Selectors**: Functions for selecting pieces of state\n5. **Effects**: Handle side effects and async operations\n\n### The NgRx Data Flow\n1. Component dispatches an Action\n2. Reducer processes the Action, creating new state\n3. Store emits new state\n4. Components receive updated state via Selectors\n\n## Setting Up NgRx\n\n### Installation\n\n```bash\n# Install the core NgRx packages\nnpm install @ngrx/store @ngrx/effects @ngrx/entity @ngrx/store-devtools @ngrx/router-store --save\n```\n\n### Basic Setup in AppModule\n\n```typescript\nimport { StoreModule } from '@ngrx/store';\nimport { EffectsModule } from '@ngrx/effects';\nimport { StoreDevtoolsModule } from '@ngrx/store-devtools';\nimport { environment } from '../environments/environment';\n\n@NgModule({\n  imports: [\n    BrowserModule,\n    StoreModule.forRoot({}, {}),\n    EffectsModule.forRoot([]),\n    StoreDevtoolsModule.instrument({\n      maxAge: 25, // Retains last 25 states\n      logOnly: environment.production,\n    }),\n  ],\n  declarations: [AppComponent],\n  bootstrap: [AppComponent],\n})\nexport class AppModule {}\n```\n\n## Actions\n\nActions are events that describe something that happened in the application.\n\n### Creating Actions\n\nUsing the modern createAction approach:\n\n```typescript\n// user.actions.ts\nimport { createAction, props } from '@ngrx/store';\nimport { User } from './user.model';\n\nexport const loadUsers = createAction('[User] Load Users');\nexport const loadUsersSuccess = createAction(\n  '[User] Load Users Success',\n  props\u003c{ users: User[] }\u003e()\n);\nexport const loadUsersFailure = createAction(\n  '[User] Load Users Failure',\n  props\u003c{ error: any }\u003e()\n);\n\nexport const addUser = createAction(\n  '[User] Add User',\n  props\u003c{ user: User }\u003e()\n);\n```\n\n### Action Best Practices\n- Use a descriptive, event-oriented name\n- Include a clear source in square brackets\n- Include relevant data in the payload\n- Keep actions granular and specific\n\n## Reducers\n\nReducers are pure functions that take the current state and an action and return a new state.\n\n### Creating Reducers\n\n```typescript\n// user.reducer.ts\nimport { createReducer, on } from '@ngrx/store';\nimport * as UserActions from './user.actions';\nimport { User } from './user.model';\n\nexport interface UserState {\n  users: User[];\n  loading: boolean;\n  error: any;\n}\n\nexport const initialState: UserState = {\n  users: [],\n  loading: false,\n  error: null\n};\n\nexport const userReducer = createReducer(\n  initialState,\n  on(UserActions.loadUsers, state =\u003e ({\n    ...state,\n    loading: true,\n    error: null\n  })),\n  on(UserActions.loadUsersSuccess, (state, { users }) =\u003e ({\n    ...state,\n    users,\n    loading: false\n  })),\n  on(UserActions.loadUsersFailure, (state, { error }) =\u003e ({\n    ...state,\n    error,\n    loading: false\n  })),\n  on(UserActions.addUser, (state, { user }) =\u003e ({\n    ...state,\n    users: [...state.users, user]\n  }))\n);\n```\n\n### Register Reducer in Module\n\n```typescript\n// user.module.ts\nimport { StoreModule } from '@ngrx/store';\nimport { userReducer } from './user.reducer';\n\n@NgModule({\n  imports: [\n    StoreModule.forFeature('users', userReducer),\n    // other imports\n  ]\n})\nexport class UserModule {}\n```\n\n### Reducer Best Practices\n- Keep reducers pure - no side effects\n- Create a new state object for each change\n- Handle each action explicitly\n- Use the spread operator for immutability\n- Split reducers by domain\n\n## Selectors\n\nSelectors are pure functions used for obtaining slices of state.\n\n### Creating Selectors\n\n```typescript\n// user.selectors.ts\nimport { createFeatureSelector, createSelector } from '@ngrx/store';\nimport { UserState } from './user.reducer';\n\nexport const selectUserState = createFeatureSelector\u003cUserState\u003e('users');\n\nexport const selectAllUsers = createSelector(\n  selectUserState,\n  (state: UserState) =\u003e state.users\n);\n\nexport const selectUserLoading = createSelector(\n  selectUserState,\n  (state: UserState) =\u003e state.loading\n);\n\nexport const selectUserError = createSelector(\n  selectUserState,\n  (state: UserState) =\u003e state.error\n);\n\n// More complex selectors can combine information\nexport const selectUserCount = createSelector(\n  selectAllUsers,\n  (users) =\u003e users.length\n);\n```\n\n### Using Selectors in Components\n\n```typescript\n// user-list.component.ts\nimport { Component, OnInit } from '@angular/core';\nimport { Store } from '@ngrx/store';\nimport { Observable } from 'rxjs';\nimport * as fromUser from './user.selectors';\nimport * as UserActions from './user.actions';\nimport { User } from './user.model';\n\n@Component({\n  selector: 'app-user-list',\n  templateUrl: './user-list.component.html'\n})\nexport class UserListComponent implements OnInit {\n  users$: Observable\u003cUser[]\u003e;\n  loading$: Observable\u003cboolean\u003e;\n  error$: Observable\u003cany\u003e;\n\n  constructor(private store: Store) {\n    this.users$ = this.store.select(fromUser.selectAllUsers);\n    this.loading$ = this.store.select(fromUser.selectUserLoading);\n    this.error$ = this.store.select(fromUser.selectUserError);\n  }\n\n  ngOnInit() {\n    this.store.dispatch(UserActions.loadUsers());\n  }\n}\n```\n\n### Selector Best Practices\n- Memoize selectors for performance\n- Keep selectors focused on specific data needs\n- Compose selectors for complex data transformations\n- Use selectors everywhere you access store state\n\n## Effects\n\nEffects isolate side effects from components, allowing for more pure components that select state and dispatch actions.\n\n### Creating Effects\n\n```typescript\n// user.effects.ts\nimport { Injectable } from '@angular/core';\nimport { Actions, createEffect, ofType } from '@ngrx/effects';\nimport { of } from 'rxjs';\nimport { catchError, map, mergeMap } from 'rxjs/operators';\nimport * as UserActions from './user.actions';\nimport { UserService } from './user.service';\n\n@Injectable()\nexport class UserEffects {\n  loadUsers$ = createEffect(() =\u003e this.actions$.pipe(\n    ofType(UserActions.loadUsers),\n    mergeMap(() =\u003e this.userService.getUsers().pipe(\n      map(users =\u003e UserActions.loadUsersSuccess({ users })),\n      catchError(error =\u003e of(UserActions.loadUsersFailure({ error })))\n    ))\n  ));\n\n  constructor(\n    private actions$: Actions,\n    private userService: UserService\n  ) {}\n}\n```\n\n### Register Effects in Module\n\n```typescript\n// user.module.ts\nimport { EffectsModule } from '@ngrx/effects';\nimport { UserEffects } from './user.effects';\n\n@NgModule({\n  imports: [\n    // other imports\n    EffectsModule.forFeature([UserEffects])\n  ]\n})\nexport class UserModule {}\n```\n\n### Effect Best Practices\n- Keep effects focused on a single responsibility\n- Handle errors properly in effects\n- Use appropriate flattening operators (mergeMap, switchMap, concatMap)\n- Avoid dispatching actions from services\n- Test effects thoroughly\n\n## Store\n\nThe Store is the central repository of state in NgRx.\n\n### Accessing the Store\n\n```typescript\n// user-detail.component.ts\nimport { Component, OnInit } from '@angular/core';\nimport { Store } from '@ngrx/store';\nimport { Observable } from 'rxjs';\nimport * as fromUser from './user.selectors';\nimport * as UserActions from './user.actions';\n\n@Component({\n  selector: 'app-user-detail',\n  templateUrl: './user-detail.component.html'\n})\nexport class UserDetailComponent implements OnInit {\n  selectedUser$: Observable\u003cUser\u003e;\n\n  constructor(private store: Store) {\n    this.selectedUser$ = this.store.select(fromUser.selectSelectedUser);\n  }\n\n  ngOnInit() {\n    // Dispatch an action to load a specific user\n    this.store.dispatch(UserActions.loadUser({ id: this.route.snapshot.params.id }));\n  }\n\n  updateUser(user: User) {\n    this.store.dispatch(UserActions.updateUser({ user }));\n  }\n}\n```\n\n### Store Best Practices\n- Organize state by feature\n- Never mutate state directly\n- Keep components as pure as possible by delegating state management to NgRx\n- Use OnPush change detection with Observable-based state\n\n## Entity State\n\nNgRx Entity provides a simple API for managing collections of entities.\n\n### Setting up Entity State\n\n```typescript\n// user.reducer.ts\nimport { createEntityAdapter, EntityAdapter, EntityState } from '@ngrx/entity';\nimport { createReducer, on } from '@ngrx/store';\nimport * as UserActions from './user.actions';\nimport { User } from './user.model';\n\nexport interface UserState extends EntityState\u003cUser\u003e {\n  selectedUserId: string | null;\n  loading: boolean;\n  error: any;\n}\n\nexport const adapter: EntityAdapter\u003cUser\u003e = createEntityAdapter\u003cUser\u003e({\n  selectId: (user: User) =\u003e user.id,\n  sortComparer: (a: User, b: User) =\u003e a.name.localeCompare(b.name),\n});\n\nexport const initialState: UserState = adapter.getInitialState({\n  selectedUserId: null,\n  loading: false,\n  error: null\n});\n\nexport const userReducer = createReducer(\n  initialState,\n  on(UserActions.loadUsers, state =\u003e ({\n    ...state,\n    loading: true,\n    error: null\n  })),\n  on(UserActions.loadUsersSuccess, (state, { users }) =\u003e \n    adapter.setAll(users, {\n      ...state,\n      loading: false\n    })\n  ),\n  on(UserActions.loadUsersFailure, (state, { error }) =\u003e ({\n    ...state,\n    error,\n    loading: false\n  })),\n  on(UserActions.addUser, (state, { user }) =\u003e \n    adapter.addOne(user, state)\n  ),\n  on(UserActions.updateUser, (state, { user }) =\u003e \n    adapter.updateOne({ id: user.id, changes: user }, state)\n  ),\n  on(UserActions.deleteUser, (state, { id }) =\u003e \n    adapter.removeOne(id, state)\n  ),\n  on(UserActions.selectUser, (state, { id }) =\u003e ({\n    ...state,\n    selectedUserId: id\n  }))\n);\n\n// Create the default selectors\nexport const {\n  selectIds,\n  selectEntities,\n  selectAll,\n  selectTotal,\n} = adapter.getSelectors();\n```\n\n### Entity Selectors\n\n```typescript\n// user.selectors.ts\nimport { createFeatureSelector, createSelector } from '@ngrx/store';\nimport * as fromUser from './user.reducer';\n\nexport const selectUserState = createFeatureSelector\u003cfromUser.UserState\u003e('users');\n\n// Using the adapter's selectors\nexport const selectUserIds = createSelector(\n  selectUserState,\n  fromUser.selectIds\n);\n\nexport const selectUserEntities = createSelector(\n  selectUserState,\n  fromUser.selectEntities\n);\n\nexport const selectAllUsers = createSelector(\n  selectUserState,\n  fromUser.selectAll\n);\n\nexport const selectUserTotal = createSelector(\n  selectUserState,\n  fromUser.selectTotal\n);\n\nexport const selectCurrentUserId = createSelector(\n  selectUserState,\n  state =\u003e state.selectedUserId\n);\n\nexport const selectCurrentUser = createSelector(\n  selectUserEntities,\n  selectCurrentUserId,\n  (userEntities, userId) =\u003e userId ? userEntities[userId] : null\n);\n```\n\n## Router Store\n\nNgRx Router Store connects the Angular Router to the NgRx Store.\n\n### Setting up Router Store\n\n```typescript\n// app.module.ts\nimport { StoreRouterConnectingModule, routerReducer } from '@ngrx/router-store';\n\n@NgModule({\n  imports: [\n    // other imports\n    StoreModule.forRoot({\n      router: routerReducer,\n    }),\n    StoreRouterConnectingModule.forRoot()\n  ]\n})\nexport class AppModule {}\n```\n\n### Creating Custom Router Serializer\n\n```typescript\n// router.serializer.ts\nimport { Params, RouterStateSnapshot } from '@angular/router';\nimport { RouterStateSerializer } from '@ngrx/router-store';\n\nexport interface RouterStateUrl {\n  url: string;\n  params: Params;\n  queryParams: Params;\n}\n\nexport class CustomSerializer implements RouterStateSerializer\u003cRouterStateUrl\u003e {\n  serialize(routerState: RouterStateSnapshot): RouterStateUrl {\n    let route = routerState.root;\n\n    while (route.firstChild) {\n      route = route.firstChild;\n    }\n\n    const {\n      url,\n      root: { queryParams },\n    } = routerState;\n    const { params } = route;\n\n    // Only return an object including the URL, params and query params\n    // instead of the entire snapshot\n    return { url, params, queryParams };\n  }\n}\n```\n\n### Configure Custom Serializer\n\n```typescript\n// app.module.ts\nimport { StoreRouterConnectingModule } from '@ngrx/router-store';\nimport { CustomSerializer } from './router.serializer';\n\n@NgModule({\n  imports: [\n    // other imports\n    StoreRouterConnectingModule.forRoot({\n      serializer: CustomSerializer\n    })\n  ]\n})\nexport class AppModule {}\n```\n\n### Creating Router Selectors\n\n```typescript\n// router.selectors.ts\nimport { createFeatureSelector, createSelector } from '@ngrx/store';\nimport { RouterReducerState, getSelectors } from '@ngrx/router-store';\nimport { RouterStateUrl } from './router.serializer';\n\nexport const selectRouter = createFeatureSelector\u003c\n  RouterReducerState\u003cRouterStateUrl\u003e\n\u003e('router');\n\nexport const {\n  selectCurrentRoute,\n  selectQueryParams,\n  selectQueryParam,\n  selectRouteParams,\n  selectRouteParam,\n  selectUrl,\n  selectFragment,\n  selectRouteData,\n} = getSelectors(selectRouter);\n\n// Custom selectors\nexport const selectRouteId = createSelector(\n  selectRouteParams,\n  params =\u003e params.id\n);\n```\n\n## DevTools\n\nNgRx DevTools provides debugging capabilities for NgRx applications.\n\n### Setting up DevTools\n\n```typescript\n// app.module.ts\nimport { StoreDevtoolsModule } from '@ngrx/store-devtools';\nimport { environment } from '../environments/environment';\n\n@NgModule({\n  imports: [\n    // other imports\n    !environment.production ? StoreDevtoolsModule.instrument({\n      maxAge: 25, // Retains last 25 states\n      logOnly: environment.production,\n      autoPause: true, // Pauses recording actions and state changes when the extension window is not open\n      trace: false, // If set to true, will include stack trace for every dispatched action\n      traceLimit: 75, // maximum stack trace frames to be stored (in case trace option was provided as true)\n    }) : []\n  ]\n})\nexport class AppModule {}\n```\n\n### Using DevTools\nOnce installed, you can:\n- Inspect state changes over time\n- Time travel through state changes\n- Dispatch actions directly\n- Export/import state for debugging\n- View action payloads\n\n## Best Practices\n\n### State Design\n1. **Normalize Data**: Use flat state structures\n2. **Minimal State**: Only store what you need\n3. **Derive Data**: Use selectors for derived data\n4. **Entity Pattern**: Use @ngrx/entity for collections\n\n### Performance\n1. **Memoized Selectors**: Use createSelector for performance\n2. **OnPush Change Detection**: Use with observables\n3. **Avoid Redundant Actions**: Don't dispatch unnecessarily\n4. **Optimize Effect Streams**: Use appropriate operators\n\n### Architecture\n1. **Feature States**: Organize by domain/feature\n2. **Facade Pattern**: Optionally use facades to abstract NgRx from components\n3. **Strong Typing**: Use TypeScript interfaces for all state\n4. **Testing**: Test actions, reducers, selectors, and effects\n\n### Organization\n1. **Feature Modules**: Align with Angular module structure\n2. **Barrel Files**: Use index.ts for public API\n3. **Clear Naming**: Follow consistent naming conventions\n4. **Action Grouping**: Group related actions\n\n## Real-World Example\n\nLet's build a simple todo application with NgRx:\n\n### Model\n\n```typescript\n// todo.model.ts\nexport interface Todo {\n  id: string;\n  text: string;\n  completed: boolean;\n  createdAt: Date;\n}\n```\n\n### Actions\n\n```typescript\n// todo.actions.ts\nimport { createAction, props } from '@ngrx/store';\nimport { Todo } from './todo.model';\n\nexport const loadTodos = createAction('[Todo] Load Todos');\nexport const loadTodosSuccess = createAction(\n  '[Todo] Load Todos Success',\n  props\u003c{ todos: Todo[] }\u003e()\n);\nexport const loadTodosFailure = createAction(\n  '[Todo] Load Todos Failure',\n  props\u003c{ error: any }\u003e()\n);\n\nexport const addTodo = createAction(\n  '[Todo] Add Todo',\n  props\u003c{ text: string }\u003e()\n);\nexport const addTodoSuccess = createAction(\n  '[Todo] Add Todo Success',\n  props\u003c{ todo: Todo }\u003e()\n);\n\nexport const toggleTodo = createAction(\n  '[Todo] Toggle Todo',\n  props\u003c{ id: string }\u003e()\n);\n\nexport const deleteTodo = createAction(\n  '[Todo] Delete Todo',\n  props\u003c{ id: string }\u003e()\n);\n```\n\n### Reducer\n\n```typescript\n// todo.reducer.ts\nimport { createEntityAdapter, EntityAdapter, EntityState } from '@ngrx/entity';\nimport { createReducer, on } from '@ngrx/store';\nimport * as TodoActions from './todo.actions';\nimport { Todo } from './todo.model';\n\nexport interface TodoState extends EntityState\u003cTodo\u003e {\n  loading: boolean;\n  error: any;\n}\n\nexport const adapter: EntityAdapter\u003cTodo\u003e = createEntityAdapter\u003cTodo\u003e({\n  selectId: (todo: Todo) =\u003e todo.id,\n  sortComparer: (a: Todo, b: Todo) =\u003e b.createdAt.getTime() - a.createdAt.getTime(),\n});\n\nexport const initialState: TodoState = adapter.getInitialState({\n  loading: false,\n  error: null\n});\n\nexport const todoReducer = createReducer(\n  initialState,\n  on(TodoActions.loadTodos, state =\u003e ({\n    ...state,\n    loading: true,\n    error: null\n  })),\n  on(TodoActions.loadTodosSuccess, (state, { todos }) =\u003e \n    adapter.setAll(todos, {\n      ...state,\n      loading: false\n    })\n  ),\n  on(TodoActions.loadTodosFailure, (state, { error }) =\u003e ({\n    ...state,\n    error,\n    loading: false\n  })),\n  on(TodoActions.addTodoSuccess, (state, { todo }) =\u003e \n    adapter.addOne(todo, state)\n  ),\n  on(TodoActions.toggleTodo, (state, { id }) =\u003e {\n    const todo = state.entities[id];\n    if (!todo) return state;\n    return adapter.updateOne(\n      {\n        id,\n        changes: { completed: !todo.completed }\n      },\n      state\n    );\n  }),\n  on(TodoActions.deleteTodo, (state, { id }) =\u003e \n    adapter.removeOne(id, state)\n  )\n);\n\nexport const {\n  selectIds,\n  selectEntities,\n  selectAll,\n  selectTotal,\n} = adapter.getSelectors();\n```\n\n### Selectors\n\n```typescript\n// todo.selectors.ts\nimport { createFeatureSelector, createSelector } from '@ngrx/store';\nimport * as fromTodo from './todo.reducer';\n\nexport const selectTodoState = createFeatureSelector\u003cfromTodo.TodoState\u003e('todos');\n\nexport const selectTodoIds = createSelector(\n  selectTodoState,\n  fromTodo.selectIds\n);\n\nexport const selectTodoEntities = createSelector(\n  selectTodoState,\n  fromTodo.selectEntities\n);\n\nexport const selectAllTodos = createSelector(\n  selectTodoState,\n  fromTodo.selectAll\n);\n\nexport const selectTodoTotal = createSelector(\n  selectTodoState,\n  fromTodo.selectTotal\n);\n\nexport const selectTodoLoading = createSelector(\n  selectTodoState,\n  state =\u003e state.loading\n);\n\nexport const selectTodoError = createSelector(\n  selectTodoState,\n  state =\u003e state.error\n);\n\nexport const selectCompletedTodos = createSelector(\n  selectAllTodos,\n  todos =\u003e todos.filter(todo =\u003e todo.completed)\n);\n\nexport const selectActiveTodos = createSelector(\n  selectAllTodos,\n  todos =\u003e todos.filter(todo =\u003e !todo.completed)\n);\n```\n\n### Effects\n\n```typescript\n// todo.effects.ts\nimport { Injectable } from '@angular/core';\nimport { Actions, createEffect, ofType } from '@ngrx/effects';\nimport { of } from 'rxjs';\nimport { catchError, map, mergeMap, switchMap } from 'rxjs/operators';\nimport { v4 as uuidv4 } from 'uuid';\nimport * as TodoActions from './todo.actions';\nimport { TodoService } from './todo.service';\n\n@Injectable()\nexport class TodoEffects {\n  loadTodos$ = createEffect(() =\u003e this.actions$.pipe(\n    ofType(TodoActions.loadTodos),\n    switchMap(() =\u003e this.todoService.getTodos().pipe(\n      map(todos =\u003e TodoActions.loadTodosSuccess({ todos })),\n      catchError(error =\u003e of(TodoActions.loadTodosFailure({ error })))\n    ))\n  ));\n\n  addTodo$ = createEffect(() =\u003e this.actions$.pipe(\n    ofType(TodoActions.addTodo),\n    mergeMap(({ text }) =\u003e {\n      const newTodo = {\n        id: uuidv4(),\n        text,\n        completed: false,\n        createdAt: new Date()\n      };\n      return this.todoService.addTodo(newTodo).pipe(\n        map(() =\u003e TodoActions.addTodoSuccess({ todo: newTodo })),\n        catchError(error =\u003e of(TodoActions.loadTodosFailure({ error })))\n      );\n    })\n  ));\n\n  toggleTodo$ = createEffect(() =\u003e this.actions$.pipe(\n    ofType(TodoActions.toggleTodo),\n    mergeMap(({ id }) =\u003e this.todoService.toggleTodo(id).pipe(\n      map(() =\u003e ({ type: '[Todo] Toggle Todo Success' })),\n      catchError(error =\u003e of(TodoActions.loadTodosFailure({ error })))\n    ))\n  ));\n\n  deleteTodo$ = createEffect(() =\u003e this.actions$.pipe(\n    ofType(TodoActions.deleteTodo),\n    mergeMap(({ id }) =\u003e this.todoService.deleteTodo(id).pipe(\n      map(() =\u003e ({ type: '[Todo] Delete Todo Success' })),\n      catchError(error =\u003e of(TodoActions.loadTodosFailure({ error })))\n    ))\n  ));\n\n  constructor(\n    private actions$: Actions,\n    private todoService: TodoService\n  ) {}\n}\n```\n\n### Component\n\n```typescript\n// todo-list.component.ts\nimport { Component, OnInit } from '@angular/core';\nimport { Store } from '@ngrx/store';\nimport { Observable } from 'rxjs';\nimport * as TodoActions from './todo.actions';\nimport * as fromTodo from './todo.selectors';\nimport { Todo } from './todo.model';\nimport { FormBuilder, FormGroup, Validators } from '@angular/forms';\n\n@Component({\n  selector: 'app-todo-list',\n  template: `\n    \u003cdiv\u003e\n      \u003ch2\u003eTodo List\u003c/h2\u003e\n      \u003cform [formGroup]=\"todoForm\" (ngSubmit)=\"onSubmit()\"\u003e\n        \u003cinput formControlName=\"text\" placeholder=\"Add a new todo...\"\u003e\n        \u003cbutton type=\"submit\" [disabled]=\"todoForm.invalid\"\u003eAdd\u003c/button\u003e\n      \u003c/form\u003e\n\n      \u003cdiv *ngIf=\"loading$ | async\"\u003eLoading...\u003c/div\u003e\n      \u003cdiv *ngIf=\"error$ | async as error\" class=\"error\"\u003e{{ error }}\u003c/div\u003e\n\n      \u003ch3\u003eActive ({{ (activeTodos$ | async)?.length }})\u003c/h3\u003e\n      \u003cul\u003e\n        \u003cli *ngFor=\"let todo of activeTodos$ | async\"\u003e\n          \u003cinput type=\"checkbox\" [checked]=\"todo.completed\" (change)=\"toggleTodo(todo.id)\"\u003e\n          {{ todo.text }}\n          \u003cbutton (click)=\"deleteTodo(todo.id)\"\u003eDelete\u003c/button\u003e\n        \u003c/li\u003e\n      \u003c/ul\u003e\n\n      \u003ch3\u003eCompleted ({{ (completedTodos$ | async)?.length }})\u003c/h3\u003e\n      \u003cul\u003e\n        \u003cli *ngFor=\"let todo of completedTodos$ | async\"\u003e\n          \u003cinput type=\"checkbox\" [checked]=\"todo.completed\" (change)=\"toggleTodo(todo.id)\"\u003e\n          \u003cspan class=\"completed\"\u003e{{ todo.text }}\u003c/span\u003e\n          \u003cbutton (click)=\"deleteTodo(todo.id)\"\u003eDelete\u003c/button\u003e\n        \u003c/li\u003e\n      \u003c/ul\u003e\n    \u003c/div\u003e\n  `,\n  styles: [`\n    .completed { text-decoration: line-through; }\n    .error { color: red; }\n  `]\n})\nexport class TodoListComponent implements OnInit {\n  todos$: Observable\u003cTodo[]\u003e;\n  activeTodos$: Observable\u003cTodo[]\u003e;\n  completedTodos$: Observable\u003cTodo[]\u003e;\n  loading$: Observable\u003cboolean\u003e;\n  error$: Observable\u003cany\u003e;\n  todoForm: FormGroup;\n\n  constructor(\n    private store: Store,\n    private fb: FormBuilder\n  ) {\n    this.todos$ = this.store.select(fromTodo.selectAllTodos);\n    this.activeTodos$ = this.store.select(fromTodo.selectActiveTodos);\n    this.completedTodos$ = this.store.select(fromTodo.selectCompletedTodos);\n    this.loading$ = this.store.select(fromTodo.selectTodoLoading);\n    this.error$ = this.store.select(fromTodo.selectTodoError);\n\n    this.todoForm = this.fb.group({\n      text: ['', [Validators.required, Validators.minLength(3)]]\n    });\n  }\n\n  ngOnInit() {\n    this.store.dispatch(TodoActions.loadTodos());\n  }\n\n  onSubmit() {\n    if (this.todoForm.valid) {\n      this.store.dispatch(TodoActions.addTodo({ text: this.todoForm.value.text }));\n      this.todoForm.reset();\n    }\n  }\n\n  toggleTodo(id: string) {\n    this.store.dispatch(TodoActions.toggleTodo({ id }));\n  }\n\n  deleteTodo(id: string) {\n    this.store.dispatch(TodoActions.deleteTodo({ id }));\n  }\n}\n```\n\nThis comprehensive guide covers the core concepts and best practices for using NgRx in Angular applications. The real-world example demonstrates how all these pieces fit together to create a maintainable, scalable application.\n\nI've created a comprehensive guide to Angular NgRx for you. Here's an overview of what's included:\n\n## What You'll Learn\n\nNgRx is a state management library for Angular based on Redux principles. It helps you manage application state in a predictable way, especially useful for medium to large applications with complex data flows.\n\nThe guide covers:\n\n1. **Core Concepts**: Store, Actions, Reducers, Selectors, and Effects\n2. **Setup and Configuration**: How to install and set up NgRx in your Angular app\n3. **Actions**: Creating and dispatching events that trigger state changes\n4. **Reducers**: Pure functions that create new state based on actions\n5. **Selectors**: Functions for efficiently selecting and deriving state\n6. **Effects**: Handling side effects like HTTP requests\n7. **Entity State**: Managing collections of data efficiently\n8. **Router Store**: Connecting Angular Router to NgRx\n9. **DevTools**: Debugging capabilities for NgRx applications\n10. **Best Practices**: Performance tips and architectural patterns\n\nThe guide finishes with a complete real-world example of a todo application using NgRx, showing how all the pieces fit together.\n\nWould you like me to explain any specific part of NgRx in more detail?\n\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftauhidul0821%2Fangular-with-ngrx","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftauhidul0821%2Fangular-with-ngrx","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftauhidul0821%2Fangular-with-ngrx/lists"}