pușigreen
pușigreen

Reputation: 131

Cannot read property 'items' of null - injecting from cart

I was trying to get the items from my shopping cart in another component so I can display them. This is what I did:

I injected the cartService and got the cart:

export class CheckoutReviewComponent implements OnInit {
  cart$: Observable<ICartModel>;

  constructor(private cartService: CartService) { }

  ngOnInit(): void {
    this.cartService.cart$ = this.cart$;
  }
}

In the html folder I looped over the items to display them:

<div class="items" *ngFor="let item of (cart$|async).items">
        <div class="product">
               **display
        </div>
</div>

In the console I get an error saying "Cannot read property 'items' of null". So I did not get them correctly. In the other component everything is working well. So what should I do to get them from the other component?

CartService:

export class CartService {
  url = 'https://localhost:5001/api/'
  private cartBS = new BehaviorSubject<ICartModel>(null);
  private totalBS = new BehaviorSubject<ICartTotal>(null);
  cart$ = this.cartBS.asObservable();
  total$ = this.totalBS.asObservable();

  constructor(private http: HttpClient) { }

  getCart(id: string) {
    return this.http.get(this.url + 'cart?cartId=' + id).pipe(
      map((cart: ICartModel)=> {
        this.cartBS.next(cart);
        this.calculateTotal();
      })
    );
  }

  updateCart(cart: ICartModel) {
    return this.http.post(this.url + 'cart', cart).subscribe(
      (response: ICartModel)=>{
         this.cartBS.next(response);
         this.calculateTotal();
        }, error => console.log(error)
      );
  }
  deleteCart(cart: ICartModel) {
    return this.http.delete(this.url + 'cart?cartId=' + cart.id).subscribe(() => {
      this.cartBS.next(null);
      this.totalBS.next(null);
      localStorage.removeItem('cart_id'); },
      error => console.log(error) );
  }

  incrementQuantity(item: ICartItem) {
    const cart = this.cartBS.value;
    const itemIndex = cart.items.findIndex(x => x.id === item.id);
    cart.items[itemIndex].quantity++;
    this.updateCart(cart);
  }

  decrementQuantity(item: ICartItem) {
    const cart = this.cartBS.value;
    const itemIndex = cart.items.findIndex(x => x.id === item.id);
    if(cart.items[itemIndex].quantity > 1) {
      cart.items[itemIndex].quantity--;
      this.updateCart(cart);
    } else {
      this.removeItem(item);
    }
  }

  removeItem(item: ICartItem) {
    const cart = this.cartBS.value;
    if ( cart.items.some(x => x.id === item.id ) ) {
      cart.items = cart.items.filter(i => i.id !== item.id);
      if ( cart.items.length > 0) {
        this.updateCart(cart);
      } else {
        this.deleteCart(cart);
      }
    }
  }

  addToCart(item: IGemModel, quantity = 1) {
    const itemAdded: ICartItem = this.mapGemToCartItem(item, quantity);
    const cart = this.cartBS.value ?? this.createCart();
    const itemIndex = cart.items.findIndex(i=> i.id === itemAdded.id);
    if(itemIndex === -1 ){
      cart.items.push(itemAdded);
    }     
    else {
      cart.items[itemIndex].quantity += quantity;
    }
    this.updateCart(cart);
  }

  mapGemToCartItem(item: IGemModel, quantity: number): ICartItem {
    return {
      id: item.id,
      name: item.name,
      price: item.price,
      quantity: quantity,
      picture: item.picture
    };
  }

  private createCart(){
    const cart = new Cart();
    localStorage.setItem('cart_id', cart.id);
    return cart;
  }

  private calculateTotal() {
    const cart = this.cartBS.value;
    const shipping =0;
    const subtotal = cart.items.reduce((a,b)=> (b.price * b.quantity) + a, 0);
    const total = subtotal + shipping;
    this.totalBS.next({shipping, subtotal, total});
  }
}

Upvotes: 1

Views: 202

Answers (1)

BizzyBob
BizzyBob

Reputation: 14750

The problem is here:

 private cartBS = new BehaviorSubject<ICartModel>(null);

You are sending an initial value of null, which your component receives.

You could either initialize to empty cart object instead of null:

 private cartBS = new BehaviorSubject<ICartModel>({items: []});

Or you can filter in your component:

this.cart$ = this.cartService.cart$.pipe(filter(c => !!c));

Upvotes: 1

Related Questions