I am trying to create/update shopping-cart in firebase. I am using a service that has functions that add the localStorage ID to firebase and It add's the quantity in shopping-cart if the product already exist otherwise it created new one. The error occurred in console TypeError: Cannot read property 'quantity' of null and I also got error on compilation in shopping-cart service.ts:
The following image demonstrate what I'm trying to get in firebase:
shopping-cart.service.ts
import { take } from 'rxjs/operators';
import { AngularFireDatabase, snapshotChanges } from 'angularfire2/database';
import { Injectable } from '@angular/core';
import { Product } from './models/product';
@Injectable({
providedIn: 'root'
})
export class ShoppingCartService {
constructor(private db: AngularFireDatabase) { }
private create(){
console.log('shoping service')
return this.db.list('/shopping-carts').push({
dateCreated: new Date().getTime()
});
}
private getCart(cartId: string){
return this.db.object('/shoping-carts/'+ cartId);
}
private async getOrCreateCartId(){
let cartId = localStorage.getItem('cartId');
if(cartId) return cartId;
let result = await this.create();
localStorage.setItem('cartId', result.key);
return result.key;
}
private getItem(cartId: string, productId: string){
return this.db.object('/shopping-carts/' + cartId + '/items/' + productId).valueChanges();
}
async addToCart(product: Product){
let cartId = await this.getOrCreateCartId();
let item$ = this.getItem(cartId, product.key);
item$.pipe(take(1)).subscribe( item => {
item$.update({ product: product, quantity: (item.quantity || 0) + 1});
});
}
shoppng-cart.service.ts (relevant part of document)
private getItem(cartId: string, productId: string){
return this.db.object('/shopping-carts/' + cartId + '/items/' + productId).valueChanges();
}
async addToCart(product: Product){
let cartId = await this.getOrCreateCartId();
let item$ = this.getItem(cartId, product.key);
item$.pipe(take(1)).subscribe( item => {
item$.update({ product: product, quantity: (item.quantity || 0) + 1});
});
}
product-card.component.ts
import { ShoppingCartService } from './../shopping-cart.service';
import { Product } from './../models/product';
import { Component, OnInit, Input } from '@angular/core';
@Component({
selector: 'product-card',
templateUrl: './product-card.component.html',
styleUrls: ['./product-card.component.css']
})
export class ProductCardComponent implements OnInit {
@Input('product') product;
@Input('show-actions') showActions = true;
constructor(private cartService:ShoppingCartService) { }
addToCart(product:Product){
this.cartService.addToCart(product);
}
ngOnInit() {
}
}
product-card.component.html
<div *ngIf="product.title" class="card m-auto">
<img class="card-img-top" [src]="product.imageUrl" *ngIf="product.imageUrl" alt="{{ product.title }}">
<div class="card-body pb-0">
<h5 class="card-title">{{product.title}}</h5>
<p>{{product.price | currency: 'USD'}}</p>
</div>
<div class="card-footer p-0 border-top">
<button *ngIf="showActions" (click)="addToCart(product)" class="btn btn-primary btn-block">Add to Cart</button>
</div>
</div>
product.ts:
export interface Product{
key: string;
title: string;
price: number;
category: string;
imageUrl: string;
}
After lot of searching and debugging also part of Yevgen answer I have modified my code to get rid of ERROR TypeError: Cannot read property 'quantity' of null. If I use valueChanges
it makes error on new cart addition. So I changed to snapshotChanges
and put some logic of it's existence and working fine now. If someone still update my answer your're most welcome.
private getItem(cartId:string, productId:string) {
return this.db.object < any > ('/shopping-carts/' + cartId + '/items/' + productId);
}
async addToCart(product:Product) {
let cartId = await this.getOrCreateCartId();
let item$ = this.getItem(cartId, product.key);
item$.snapshotChanges().pipe(take(1)).subscribe((item:any) => {
if (item.key != null) {
item$.update( {quantity:( item.payload.val().quantity || 0) + 1});
}
else{
item$.set( {product:product, quantity:1});
}
});
}
Error is pretty descriptive observable has no such property. Its because valueChanges()
function returns an observable and it has only data in it. But AngularFireObject
has update function and you need to use it. So you need to modify your code like:
private getItem(cartId: string, productId: string): {
return this.db.object<any>('/shopping-carts/' + cartId + '/items/' + productId);
}
async addToCart(product: Product){
let cartId = await this.getOrCreateCartId();
let item$ = this.getItem(cartId, product.key);
item$.valueChanges().pipe(take(1)).subscribe((item: any) => {
item$.update({ product: product, quantity: (item.quantity || 0) + 1});
});
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With