【问题标题】:angular ctx.cart.getQuantity is not a function角度 ctx.cart.getQuantity 不是函数
【发布时间】:2020-05-10 17:24:50
【问题描述】:

我正在使用 Angular 实现一个简单的水果店 Web 应用程序,我在其中遇到了一个奇怪的异常,我已经花了很多时间,但还没有找到任何解决方案。

整体应用的场景如下:

在根路径(页面)中,我正在加载一堆水果(每个水果显示为Bootstrap Card),最终用户可以从中选择并将它们添加到他/她的购物车(通过Add to Card-+ 按钮);然后他/她可以去他/她的购物车进行结帐和付款过程。

我重要实现的components如下:

  • products(在app组件中作为主组件加载,起到应用的主页面作用)
  • productCard(扮演引导卡角色并使用*ngFor 加载到products 组件中
  • productQuantity(它在productCard组件内部提供了Add to Card-+按钮。我已经将这个功能实现为一个组件,因为我想在购物车页面中使用这个功能,允许结束- 也用于在此处添加或减去他/她的选择)

我还实现了一些简单的模型来扮演实体角色,如下所示:

  • cart.ts(该类最重要的属性和方法是CartItem[]数组,用于将最终用户选择的商品保存在他/她的购物车中,还有getQuantity(product: Product)方法,用于获取产品并返回一个number 指当时最终用户购物车中该产品的总数)
  • cart-item.ts(这个类最重要的属性是QuantityProduct属性)
  • product.ts(该类最重要的属性是NameFee 属性。

这里的代码对你来说更清楚:

Products Component Class:

import { Component, OnInit, OnDestroy } from '@angular/core';
import { ProductService } from '../product.service';
import { Product } from '../models/product';
import { Subscription } from 'rxjs';
import { AuthService } from '../auth.service';
import { Cart } from '../models/cart';
import { ShoppingCartService } from '../shopping-cart.service';

@Component({
  selector: 'app-products',
  templateUrl: './products.component.html',
  styleUrls: ['./products.component.css']
})
export class ProductsComponent implements OnInit {
  products: Product[];
  filteredProducts: Product[];
  productsSubscription: Subscription;
  cartSubscription: Subscription;
  cart: Cart;

  constructor(
    private productService: ProductService,
    public auth: AuthService,
    private cartService: ShoppingCartService) {

  }

  filter(query: string) {
    this.filteredProducts = (query) ?
      this.products.filter(p => p.Name.toLowerCase().includes(query.toLowerCase())) :
      this.products;
  }

  async ngOnInit() {
    if (this.auth.isLoggedIn()) {
      (await this.cartService.getCart())
        .subscribe(cart => {
          this.cart = cart;

          this.productService.getAll().then(v =>
            v.subscribe(products => {
              this.filteredProducts = this.products = products
            }));
        });
    }
  }
}

Products Component Markup:

<h4>Welcome to Fruit Shop. <ng-container *ngIf="!auth.isLoggedIn()"> To purchase fruits you must <span>log in first</span>.</ng-container></h4>
<p>
    <input type="text" #query (keyup)="filter(query.value)" 
        placeholder="Type here to search among products..." class="form-control">
</p>
<div class="row">
    <ng-container *ngFor="let p of filteredProducts">
        <div class="col">
            <product-card [product]="p" [cart]="cart"></product-card>
        </div>
    </ng-container>
</div>

ProductCard Component Class:

import { Component, OnInit, Input } from '@angular/core';
import { Product } from '../models/product';
import { ShoppingCartService } from '../shopping-cart.service';
import { AuthService } from '../auth.service';
import { Cart } from '../models/cart';

@Component({
  selector: 'product-card',
  templateUrl: './product-card.component.html',
  styleUrls: ['./product-card.component.css']
})
export class ProductCardComponent {
  @Input('product') product: Product;
  @Input('cart') cart: Cart;

  constructor(public cartService: ShoppingCartService, public auth: AuthService) {

  }

  async addToCart() {
    this.cartService.addToCart(this.product).subscribe(
      res => this.cartService.getCart().then(
        res => {
          res.subscribe(c => this.cart = c)
        }).then(value => this.cartService.totalCartItems += 1)
    );
  }
}

ProductCard Component Markup:

<div class="card" style="width: 15rem;">
    <img class="card-img-top" [src]="product.imageUrl" alt="Card image cap">
    <div class="card-body">
        <h5 class="card-title">{{ product.Name }}</h5>
        <span class="card-text">{{ product.Fee | currency }}</span>
        <button 
            *ngIf="cart && cart.getQuantity(product) > 0" 
            (click)="cartService.deleteFromCart(product)" 
            type="button" class="btn btn-danger btn-sm">Delete</button>
    </div>
    <div class="card-footer">
        <button *ngIf="!auth.isLoggedIn() || (cart && cart.getQuantity(product)) === 0; else updateQuantity"
            [ngClass]="!auth.isLoggedIn() ? 'button-not-allowd' : 'button-cursor'" 
            [disabled]="!auth.isLoggedIn()"
            (click)="addToCart() " class="btn btn-primary btn-block ">Add to Cart</button>

        <ng-template #updateQuantity>
            <product-quantity [product]="product" [cart]="cart"></product-quantity>
        </ng-template>
    </div>
</div>

ProductQuantity Component Class:

import { Component, OnInit, Input } from '@angular/core';
import { Product } from '../models/product';
import { Cart } from '../models/cart';
import { ShoppingCartService } from '../shopping-cart.service';

@Component({
  selector: 'product-quantity',
  templateUrl: './product-quantity.component.html',
  styleUrls: ['./product-quantity.component.css']
})
export class ProductQuantityComponent {

  @Input('product') product: Product;
  @Input('cart') cart: Cart;

  constructor(private cartService: ShoppingCartService) { 

  }

  async addToCart() {
    this.cartService.addToCart(this.product).subscribe(
      res => this.cartService.getCart().then(
        res => {
          res.subscribe(c => this.cart = c)
        }).then(value => this.cartService.totalCartItems += 1)
    );
  }

  subtractFromCart() {
    this.cartService.subtractFromCart(this.product).subscribe(
      res => this.cartService.getCart().then(
        res => res.subscribe(c => this.cart = c)).then(value => this.cartService.totalCartItems -= 1)
    );
  }

  deleteFromCart() {
    this.cartService.deleteFromCart(this.product).subscribe(
      res => this.cartService.getCart().then(
        res => res.subscribe(c => this.cart = c))
          .then(value => this.cartService.totalCartItems -= 1)
    );
  }
}

ProdcutQuantity Component Markup:

<div class="row no-gutters">
    <div class="col-2 ">
        <button (click)="cart.getQuantity(product) > 1 ? subtractFromCart() : deleteFromCart()"
            class="btn btn-secondary btn-block ">-</button>
    </div>
    <div class="col text-center quantity-text">
        {{ cart.getQuantity(product) }} in cart
    </div>
    <div class="col-2 ">
        <button (click)="addToCart()" class="btn btn-secondary btn-block ">+</button>
    </div>
</div>

Cart Service 是一项服务,用于与应用程序的服务器端就购物车相关的事情进行通信:

import { Injectable } from '@angular/core';
import { Product } from './models/product';
import { AuthService } from './auth.service';
import { Router } from '@angular/router';
import { Cart } from './models/cart';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Observable, of } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class ShoppingCartService {
  totalCartItems: number = 0;

  constructor(private auth: AuthService, private router: Router, private http: HttpClient) {
    if (auth.isLoggedIn()) {
      this.getCart().then(resp =>
        resp.subscribe(cart => cart.CartItems.forEach(item => this.totalCartItems += item.Quantity)))
    }
  }

  addToCart(product: Product) {
    const httpOptions = {
      headers: new HttpHeaders({
        'Content-Type': 'application/json',
        'Authorization': 'Bearer ' + localStorage.getItem('token')
      })
    };

    return this.http.post(
      `http://localhost/FruitShop/api/v1/CartItems/AddItemByItemId/?productId=${product.Id}`,
      {},
      httpOptions);
  }

  subtractFromCart(product: Product) {
    const httpOptions = {
      headers: new HttpHeaders({
        'Content-Type': 'application/json',
        'Authorization': 'Bearer ' + localStorage.getItem('token')
      })
    };

    return this.http.post(
      `http://localhost/FruitShop/api/v1/CartItems/SubstractItemByItemId/?productId=${product.Id}`,
      {},
      httpOptions);
  }

  deleteFromCart(product: Product) {
    const httpOptions = {
      headers: new HttpHeaders({
        'Content-Type': 'application/json',
        'Authorization': 'Bearer ' + localStorage.getItem('token')
      })
    };

    return this.http.post(
      `http://localhost/FruitShop/api/v1/CartItems/DeleteItemByItemId/?productId=${product.Id}`,
      {},
      httpOptions);
  }

  async getCart(): Promise<Observable<Cart>> {
    const httpOptions = {
      headers: new HttpHeaders({
        'Content-Type': 'application/json',
        'Authorization': 'Bearer ' + localStorage.getItem('token')
      })
    };

    return this.http.get<Cart>('http://localhost/FruitShop/api/v1/Cart/GetCart', httpOptions);
  }
}

cart.ts 这是我的购物车模型:

import { CartItem } from './cart-item';
import { Product } from './product';

export class Cart {
    Id: number;
    IsActive: Boolean;
    IsDeleted: Boolean;
    CreatedAt: Date;
    User_Id: number;
    Coupons_Id: number;
    CartItems: CartItem[];

    getQuantity(product: Product): number {
        let count: number = 0;

        this.CartItems
            .filter(ci => ci.Products_Id == product.Id)
            .forEach(i => {
                count += i.Quantity;
            });

        return count;
    }
}

cart-item.ts 这是我的 CartItem 模型:

import { Product } from './product';

export class CartItem {

    constructor(
        public Id: number,
        public Quantity: number,
        public ModifiedAt: Date,
        public IsDeleted: Boolean,
        public Products_Id: number,
        public Carts_Id: number,
        public Products: Product
    )
    {}

    get totalPrice() {
        return this.Quantity * this.Products.Fee;
    }
}

product.ts 这是我的产品型号:

export class Product {

    constructor(public Id: number, public Name: string, public Fee: number, public imageUrl: string){

    }
}

Here is the exception我不知道怎么解决,在网上搜索了很多谷歌后:

core.js:6185 ERROR TypeError: ctx.cart.getQuantity is not a function
    at ProductCardComponent_Template (product-card.component.html:7)
    at executeTemplate (core.js:11949)
    at refreshView (core.js:11796)
    at refreshComponent (core.js:13229)
    at refreshChildComponents (core.js:11527)
    at refreshView (core.js:11848)
    at refreshDynamicEmbeddedViews (core.js:13154)
    at refreshView (core.js:11819)
    at refreshComponent (core.js:13229)
    at refreshChildComponents (core.js:11527)

【问题讨论】:

  • 你能分享这个的 stackblitz URL 吗?
  • @prathameshk73,我还没有把项目放到 stackblitz 上。

标签: javascript angular typescript


【解决方案1】:

我想问题是cart 不是立即可用的,而是来自Products Component Class 中的subscription。这意味着在一开始的时候它是undefinedgetQuantity 不是一个函数。

尝试下一个解决方法:在div.row 上添加ngIf

Products Component Markup:

<h4>Welcome to Fruit Shop. <ng-container *ngIf="!auth.isLoggedIn()"> To purchase fruits you must <span>log in first</span>.</ng-container></h4>
<p>
    <input type="text" #query (keyup)="filter(query.value)" 
        placeholder="Type here to search among products..." class="form-control">
</p>
<div class="row" *ngIf="cart">
    <ng-container *ngFor="let p of filteredProducts">
        <div class="col">
            <product-card [product]="p" [cart]="cart"></product-card>
        </div>
    </ng-container>
</div>

现在只有当购物车存在且不应导致错误时才会呈现产品购物车。

【讨论】:

  • 我这样做了,但问题仍然存在。
  • magic :) 我是否理解错误出现在product-card 组件中?
  • 是的,完全正确。这对我来说也很奇怪。
  • 最后我通过实例化购物车解决了这个问题。例如 this.cart = new Cart(c.Id, c.IsActive, c.IsDeleted, c.CreatedAt, c.User_Id, c.Coupons_Id, c.CartItems),而不是 this.cart = c.在我需要这样的东西的所有情况下,我都这样做了。
  • 太棒了!很高兴听到。
猜你喜欢
  • 2020-05-09
  • 2021-05-02
  • 2020-06-20
  • 1970-01-01
  • 2018-04-19
  • 2016-11-07
  • 1970-01-01
  • 2020-07-18
  • 1970-01-01
相关资源
最近更新 更多