angularngrxngrx-storeangular18angular-state-managmement

Cannot read properties of undefined (reading 'product') while trying to add items to cart using NGRX in Angular 18-


I am trying to understand implementation of ngRX in Angular18. Trying to build a shopping-cart flow. There's an 'Add To Cart' button with each gadget listed upon clicking which the gadget SHOULD get added to cart. There's a cart widget whose items counter SHOULD get updated upon addition of gadgets. Both my 'Add To Cart' and 'Cart Widget' are reusable components. Currently, my app is behaving in a very funny way. The first item I add, gets added to cart and the cart item count updates from 0 to 1. But from the 2nd item to add, I get an error:

Cannot read properties of undefined (reading 'product')

I'll enlist the related code blocks so you may see for yourself.

cart.action.ts -

import { createAction, props } from "@ngrx/store";
import { CartItem } from "../shared/models/cart.item";


export const addToCart=createAction(
    `[Gadget] Add To Cart`, 
    props<{gadget:CartItem}>()
);

cart.item.ts -

export interface CartItem {
  product: Gadget;
  quantity: number;
}

gadget.model.ts -

export interface Gadget {
    id: number;
    name: string;
    distributor: string;
    description: string;
    price: number;
    available: boolean;
    rating?: number;
}

cart.reducer.ts -

export const initialState:CartState ={
    cart:[]
}

export const cartReducer=createReducer(
    initialState,

    on(addToCart, (state:any, {gadget}) => {
        const existingGadget = state.cart.find((i:any)=>i.product.id === gadget.product.id);   //error here
        if(existingGadget)
        {
            return {
                ...state,
                cart: state.cart.map((i:any)=>
                    i.product.id === gadget.product.id
                    ?{...i, quantity: i.quantity + gadget.quantity }
                    : i
                )
            };
        }
        else
        {
            return {...state, cart: [...state.cart, gadget]};
        }
    })
)

cart.state.ts -

export interface CartState {
    cart:CartItem[]
}

Inside my AddToCart component -

export class AddGadgetComponent implements OnInit {
    @Input() gadget: any;

    constructor(private store:Store)
    {
      
    }

    addGadget()
    {
        console.log("Adding gadget to cart", this.gadget.id);
        this.store.dispatch(addToCart(this.gadget));
    }
}

Inside my cart widget component -

export class CartWidgetComponent implements OnInit {

    cartItemCount: number = 0; 
  
    cart: any[] = [];

    constructor(private store: Store<AppState>, private router:Router)
    {
    
    }

    ngOnInit(): void {
   
    this.store.select('cart').subscribe((res: any) => {
      this.cart = res.cart;
      this.cartItemCount = res.cart.length;
    });
    
  }
}

Lastly my AppState is -

export interface AppState {
  gadget: GadgetState;
  cart: CartState;
}

export const reducers: ActionReducerMap<AppState> = {
  gadget: gadgetReducer,
  cart: cartReducer,
};

I think I have posted the related and relevant code blocks related to my flow. I have also marked the line in cart.reducer.ts which is SUPPOSEDLY throwing the error. It is the line where I am trying to find out if gadget with same id is being added or not. Please go through my code slowly and carefully. If you need more insight pls do ask, will gladly share. I'm unable to pinpoint the source of the glitch. I have a feeling it's something small but crucial in the context.

Thanks in advance,


Solution

  • The problem is in the format of the object you are sending when triggering the NGRX action.

    Here, you are sending only the gadget (probably of type Gadget), and not a CartItem. That is why, in the reducer, when trying to access gadget.product, it gives an error, because gadget does not have the product property.

    To fix it, go to addGadget(), do this:

    addGadget() {
    const cartItem: CartItem = { product: this.gadget, quantity: 1 }; this.store.dispatch(addToCart({ gadget: cartItem }));
    }