Search code examples
javascriptangulartypescriptdata-bindingctx

TypeError: "ctx.cart is undefined"


I'm working on pizza ordering app, and currently I'm trying to implement Cart.

I have a cart for every user, and each cart contains following details: cart.ts

import { CartItem } from './cartitem';

export class Cart {
    id: string;
    user: string;
    cartitems: CartItem[];
    grand_total: number;
}

User can add items from the menu, which will become my cart items: cartitem.ts

import { Topping } from './topping';
export class CartItem {
    id: string;
    name: string;
    baseprice: number;
    toppings: Topping[];
    extraprice: number;
    quantity= 1;
    total: number;
}

So on adding an item to the cart, I'm making a PUT request on the API endpoint on the user cart, to put the cart item, and in response, I'm returning the cart contents from there.

Here's how my cart.component.ts looks:

import { Component, OnInit, ViewChild, Inject, Injector } from '@angular/core';
import { CartItem } from '../shared/cartitem';
import { ActivatedRoute } from '@angular/router';
import { CartService } from '../services/cart.service';
import { map, catchError } from 'rxjs/operators';
import { Cart } from '../shared/cart';

@Component({
  selector: 'app-cart',
  templateUrl: './cart.component.html',
  styleUrls: ['./cart.component.scss']
})
export class CartComponent implements OnInit {

  cart: Cart;
  // cartitems: CartItem[];

  constructor(
    private route: ActivatedRoute,
    private cartService: CartService,
    @Inject('BaseURL')public BaseURL) { }


  ngOnInit(): void {
    this.updateCart();

  }

  updateCart() {
    this.cartService.mysubject.subscribe((value) => {
      console.log(value);
      this.cartService.getItems().subscribe(response => {
        console.log("Response in cart comp:", response);

        let cartContent = new Cart();
        cartContent.cartitems = response['cartitems'];
        cartContent.id = response['id'];
        cartContent.grand_total = response['grand_total'];
        cartContent.user = response['user'];

        this.cart = cartContent;

        console.log("Cart is:", this.cart);

      });
    });
  }

}

Now issue here is that I'm not able to bind the data on cart.component.html side with 'cart', it generates this error- ERROR TypeError: "ctx.cart is undefined".

I'm clueless on how to fix this.

Edit: Here is the cart.component.html:

<h2>Cart</h2>
<div>{{cart.user}}</div>
<mat-list dense>
    <mat-list-item *ngFor="let cartitem of cart.cartitems">
        <h2 matLine>Item: {{cartitem.name}}</h2>
        <p matLine>Base Price: ${{cartitem.baseprice}}</p>
        <p matLine [hidden]="cartitem.toppings == []">Extra Toppings:
            <mat-list matLine>
                <mat-list-item matLine *ngFor="let topping of cartitem.toppings">
                    <h4 matLine>{{topping.name}} : + ${{topping.rate}}</h4>
                </mat-list-item>
            </mat-list>
        </p>
        <button mat-mini-fab color="primary"><i class="fa fa-minus-circle"></i></button><div></div><button mat-mini-fab color="primary"><i class="fa fa-plus-circle"></i></button>
    </mat-list-item>

</mat-list>

<div [hidden]="!cart.cartitems"><h2>Subtotal: ${{cart.grand_total}}</h2></div>

<div [hidden]="cart.cartitems">
    <p> Your Cart is Empty!</p>
</div>

and error log:

ERROR TypeError: "ctx.cart is undefined"
    CartComponent_Template cart.component.html:2
    Angular 26
        executeTemplate
        refreshView
        refreshComponent
        refreshChildComponents
        refreshView
        refreshComponent
        refreshChildComponents
        refreshView
        refreshDynamicEmbeddedViews
        refreshView
        refreshComponent
        refreshChildComponents
        refreshView
        renderComponentOrTemplate
        tickRootContext
        detectChangesInRootView
        detectChanges
        tick
        next
        invoke
        onInvoke
        invoke
        run
        run
        next
        schedulerFn
    RxJS 5
    Angular 8
core.js:6185:19


Solution

  • The HTML added shows the error in line 2 of HTML

    <div>{{cart.user}}</div>
    

    cart is defined as in your TS file

    cart: Cart;
    

    The interpolated value cart.user is not available as cart itself is undefined at time of init. cart is populated later when subscription resolves.

    A quick work-around maybe to wrap this in an ng-container and use *ngIf on it.

    <ng-container *ngIf="cart">
      <h2...
      ...
      </div>
    </ng-container>
    

    This will not display anything till the cart is resolved.

    However, in terms of better code quality, replace all your interpolations with getters in the TS

    get cartUser() { return (cart && cart.user) ? cart.user : null }
    

    Now your HTML will be like

    <div>{{cartUser}}</div>
    

    Same way, you will get error for cart.cartitems. Use this getter

    get cartCartitems() { return (cart && cart.cartitems) ? cart.cartitems : [] }
    

    and replace all instances of cart.cartitems with cartCartitems in your HTML

    Hope it helps!