Search code examples
angularngrxngrx-store

NGRX selector not executing and no subscription in component


Hello to any senior developper with experience in NGRX.

Im trying to implement a Task Manager app using ngrx as a personal project for learning NgRx, howevwer when dispatching the event from the store my data flow does not get completed. Im new to NgRx so can somebody explain to me what am I doing wrong?

I have a button which opens a dialog with a simple input field, after the submit button is clicked I want to add that new item into the items array however I either get 'undefined' or I get nothing at all.

Here is all the pieces of code that are interacting between them.

app.state.ts

import { MenuState } from "../states/reducers/menu.reducer";

export interface AppState {
    menuState:MenuState; // I want to add more states here for Tasks, Subtasks and so on
}

index.ts for multiple reducers

// This file is imported in app.module.ts
import { menuReducer } from './menu.reducer';

export const reducers = {
  menu: menuReducer // More reducers will be added here
};

app.module.ts

// NGRX Imports
import { reducers } from './states/reducers';
import { StoreModule } from '@ngrx/store';

  imports: [
    BrowserModule,
    AppRoutingModule,
    FormsModule,
    ReactiveFormsModule,
    MaterialModule,
    AtomsModule,
    StoreModule.forRoot(reducers)
  ],

NewBoardDialogComponent

  onSubmit(){
    let newMenuItem: MenuItem = {
      menuItemIcon: 'table_chart',
      menuItemType: MenuItemType.DISPLAY_BOARD,
      boardTitle: this.newBoardForm.value['boardTitle'].trim(),
    }
    this.store.dispatch(addMenuItem({menuItem: newMenuItem})); // Executes properly
    this._modalService.close();
  } 

menu.actions.ts

import { createAction, props } from '@ngrx/store';
import { MenuItem } from '../../models/menuItem.model';


export const removeMenuItems = createAction(
  '[Menu Item] Remove Menu Items',
  props<{ menuItem: MenuItem }>()
);
  
export const addMenuItem = createAction(
  '[Sidenav] Add Menu Items',
  props<{ menuItem: MenuItem }>()
);

export const loadMenuItems= createAction('[MenuItem] Load Menu Items');

menu.reducer.ts

import { Action, createReducer, on } from '@ngrx/store';
import { MenuItem } from "../../models/menuItem.model";
import {addMenuItem, loadMenuItems} from '../actions/menu.actions'

export interface MenuState {
    menuItems: MenuItem[];
    error: string | undefined;
    status: string;
}

export const initialState: MenuState = {
    menuItems: [],
    error: '',
    status: 'pending'
}

export const menuReducer = createReducer(
    initialState,
    on(addMenuItem, (state, action) => ({
        ...state,
        menuItems: [...state.menuItems, action.menuItem ], // I can see the new Menu Item data here
      })),
    on(loadMenuItems, (state) => ({ ...state, status: 'loading' })),
)

export const featureKey = 'MenuItemsState';

menu.selector.ts

import { createFeatureSelector, createSelector } from '@ngrx/store';
import { AppState } from '../app.state';
import { MenuState, featureKey } from '../reducers/menu.reducer';

export const selectMenuItems = (state: AppState) => state.menuState;
export const getViewState = createFeatureSelector<MenuState>(featureKey );
export const selectAllMenuItems = createSelector(
    selectMenuItems,
  (state: MenuState) => state?.menuItems
);

sidenav.component.ts

 ngOnInit(): void {
    this.store.select(selectAllMenuItems).subscribe({ // Not triggering, but here i am supposed to see the updated manuItems array
      next: (payload)=>{
        console.log(payload);
      },
      error: (error)=>{
        console.log(error);
      }
    });
  }

Solution

  • The AppState don't match with the reducers (which built the state).

    Either change:

    The state and feature selector:

    export interface AppState {
        menu:MenuState;
    }
    export const selectMenuItems = (state: AppState) => state.menu;
    

    Or the reducers:

    
    export const reducers = {
      menuState: menuReducer 
    };