Razvan Zamfir
Razvan Zamfir

Reputation: 4684

What causes the cart service failure in this Angular 13 app?

I am working on an e-commerce app who's front-end is made in Angular 13.

I use a CartService service to add products to the cart (app\services\cart.service.ts):

import { Injectable } from '@angular/core';
import { Subject } from 'rxjs';
import { Product } from '../models/product';

@Injectable({
  providedIn: 'root'
})
export class CartService {

  subject = new Subject();
  product: Product | undefined;

  constructor() { }

  sendProductTocart(product: Product) {
    this.subject.next(product);
  }
}

In app\components\product-item\product-item.component.ts I have:

import { Component, OnInit, InputDecorator, Input } from '@angular/core';
import { Product } from '../../models/product';
import { CartService } from '../../services/cart.service';

@Component({
  selector: 'app-product-item',
  templateUrl: './product-item.component.html',
  styleUrls: ['./product-item.component.css']
})
export class ProductItemComponent implements OnInit {

  @Input() product!: Product;
  @Input() handleAddToCart!: (args: any) => void;

  constructor(private cartService: CartService) { }

  addToCart(product: Product) {
    console.log(this.cartService.product);
  }

  ngOnInit(): void {
  }

}

In app\components\add-to-cart\add-to-cart.component.ts I have:

import { Component, OnInit, InputDecorator, Input } from '@angular/core';
import { Product } from '../../models/product';

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

  @Input() product!: Product;
  @Input() handleAddToCart!: (args: any) => void;

  constructor() { }

  ngOnInit(): void {
  }

}

In app\components\add-to-cart\add-to-cart.component.html:

<button class="btn btn-sm btn-success w-100" (click)="handleAddToCart(product)">Add</button>

The goal

The goal is to add products to the top-cart component (app\components\top-cart\top-cart.component.ts), more exactly, to the cartItems array:

import { Component, OnInit } from '@angular/core';

@Component({
  selector: '.app-top-cart',
  templateUrl: './top-cart.component.html',
  styleUrls: ['./top-cart.component.css']
})
export class TopCartComponent implements OnInit {
  cartItems: any = [
    
  ];

  constructor() { }

  totalPrice: number = 0;

  doTotalPrice() {
    let total = 0;
    this.cartItems.forEach((item: { price: number, quantity: number }) => {
      item.quantity = 1;
      total += item.price * item.quantity
    });
    this.totalPrice = total;
  }

  ngOnInit(): void {
    this.doTotalPrice();
  }

}

Stackblitz

See Stackblitz demo here.

The problem

For a reason I have not been able to find out, when I click the "Add to cart" button in the product list, I get this error in the Ghrome console:

Cannot read properties of undefined

Where is my mistake?

Upvotes: 1

Views: 454

Answers (2)

Andrew Allen
Andrew Allen

Reputation: 8042

You're doing this, passing function down to child

  <div class="card-footerer p-1">
    <app-add-to-cart [handleAddToCart]="addToCart"></app-add-to-cart>
  </div>

But in your child component

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

  @Input() product!: Product;
  @Input() handleAddToCart!: (args: any) => void;

  constructor() { }

  ngOnInit(): void {
  }

}

the service cartService is not injected into this child component. this is not passed down but is referring now to AddToCartComponent. And this.cartService is not defined. So it's throwing error ERROR Error: this.cartService is undefined (in firefox)

Alternative

Use an event emitter to pass the data up and handle event in parent.

export class AddToCartComponent {
  @Input() product!: Product;
  @Output() productAdded: EventEmitter<Product> = new EventEmitter();

  constructor() { }

  ngOnInit(): void {
  }

  handleAddToCart(product: Product) {
      this.productAdded.emit(product)
  }
}
 <app-add-to-cart (productAdded)="addToCart($event)"></app-add-to-cart>

Upvotes: 1

Charlie V
Charlie V

Reputation: 1040

You need to add the product:

now:

        <div class="card-footerer p-1">
                <app-add-to-cart [handleAddToCart]="addToCart"></app-add-to-cart>
        </div>

should be:

        <div class="card-footerer p-1">
                <app-add-to-cart [handleAddToCart]="addToCart"
                                 [product]="product"></app-add-to-cart>
        </div>

Upvotes: 1

Related Questions