【发布时间】: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(这个类最重要的属性是
Quantity和Product属性) - product.ts(该类最重要的属性是
Name和Fee属性。
这里的代码对你来说更清楚:
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