【问题标题】:Angular 12 subscriptions not behaving as expectedAngular 12 订阅未按预期运行
【发布时间】:2021-10-29 13:14:15
【问题描述】:

我在 Angular 12 中使用 Firestore 作为后端实现了 ShoppingCart 服务。我的目标是在整个应用程序中坚持购物。当页面加载时,有一种方法可以检查localStorage 中的cartId 字段。如果它不存在,该方法会在 Firestore 中创建一个新的购物车,并将该 ID 作为 cartId 存储到 localStorage 中。我有两个组件通过订阅valuechanges() 使用此购物车服务。因为两个组件同时(或几乎)加载,所以订阅没有按预期发生。两个组件都执行各自的订阅并将 cartId 返回为 null,随后,我得到了 2 个新的购物车项目。

这是来自 codewithmosh.com 的一个项目(注意:他在实现中使用了 Angular 4 和 Firebase)。我正在接受使用 Firestore 将其更新到 Angular12 的挑战。

ShoppingCartService

import { take } from 'rxjs/operators';
import { AngularFirestore} from '@angular/fire/firestore';
import { Injectable } from '@angular/core';
import { Product } from './models/app.product';
import { ShoppingCart } from './models/shopping-cart';
import { Observable } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class ShoppingCartService {
  
  constructor(private afs: AngularFirestore) { }
  
  private async getOrCreateCartId(): Promise<string>{
    let cartId = localStorage.getItem('cartId');
    if (cartId) return cartId;

    let result: any = await this.create();
    localStorage.setItem('cartId', result.id);
    return result.id;  
  }
  private create() {
    return this.afs.collection('shopping-carts').add({
      dateCreated: new Date().getTime()
    });
  }

  async getCartItems(): Promise<Observable<ShoppingCart[]>>{
    let cartId = await this.getOrCreateCartId();
    return this.afs.collection('shopping-carts').doc(cartId).collection<ShoppingCart>('items').valueChanges();

  }
    async getCart() {
    let cartId = await this.getOrCreateCartId();
    return this.afs.collection('shopping-carts').ref.doc(cartId).get();

  }
  private getItem(cartId: string, productId: string) {
    return this.afs.collection('shopping-carts').doc(cartId).collection('items').doc(productId);
  }

  async updateItemQuantity(product: Product, change: number) {
    let cartId = await this.getOrCreateCartId();
    // check for a reference for this product in the current shopping cart
    // if there is no reference, add it and set quantity to 1
    // otherwise, increment the quantity
    let item$ = this.getItem(cartId, product.id);
    item$.get().pipe(take(1)).subscribe((item) => {
         item$.set ({ product: product, quantity: ((item.data()?.quantity || 0)  + change) }) 
      });
  }
  async removeFromCart(product: Product) {
    this.updateItemQuantity(product, -1);
  }

  async addToCart(product: Product) {
    this.updateItemQuantity(product, 1);
  }


  getTotalItemsCount(cart: ShoppingCart[]) {
        let count = 0;
        cart.forEach((item: any) => {
            count += item.quantity;
        })
        return count;
    
  }
}

导航栏组件:

import { ShoppingCartService } from './../shopping-cart.service';
import { UserService } from './../user.service';
import { AuthService } from './../auth.service';
import { Component, OnInit } from '@angular/core';
import { AppUser } from '../models/app.user';

@Component({
  selector: 'bs-navbar',
  templateUrl: './bs-navbar.component.html',
  styleUrls: ['./bs-navbar.component.css']
})
export class BsNavbarComponent implements OnInit {
  miniMenuToggle = false;
  isAdmin = false;
  username = '';
  shoppingCartItemCount: number = 0;

  constructor(
    public auth: AuthService,
    private userService: UserService,
    private cartService: ShoppingCartService) {
    
    this.userService.getUsername().subscribe(
      (user: AppUser) => {
        this.username = user.name;
        this.isAdmin = user.isAdmin;
      }
    );
  }

  async ngOnInit() {
  
    let cart$ = (await this.cartService.getCartItems());
    cart$.subscribe(items => {
      this.shoppingCartItemCount = 0;
      items.forEach((item: any) => {
        this.shoppingCartItemCount += item.quantity;
      })
    })
  
  }
  toggle() {
    this.miniMenuToggle = !this.miniMenuToggle;
  }
  logout() {
    this.auth.logout();
  }

}

产品组件:

import { ProductId } from './../models/app.product';
import {  ShoppingCartService } from './../shopping-cart.service';
import {  switchMap } from 'rxjs/operators';
import { ActivatedRoute } from '@angular/router';
import { ProductService } from './../product.service';
import { Component, OnInit, OnDestroy } from '@angular/core';
import { Product } from '../models/app.product';
import { Subscription } from 'rxjs';


@Component({
  selector: 'products',
  templateUrl: './products.component.html',
  styleUrls: ['./products.component.css']
})
export class ProductsComponent implements OnInit, OnDestroy{
  products: ProductId[] = [];
  filteredProducts: Product[] = []
  category: string | null = '';
  showActions = false;
  cart: any;
  subcription= new  Subscription;


  constructor(
    productService: ProductService,
    private shoppingCartService: ShoppingCartService,
    route: ActivatedRoute) {
    
    productService.getAll().pipe(
      switchMap(products => {
        this.products = products
        return route.queryParamMap;
      })
     ).subscribe(params => {
        this.category = params.get('category');
      
        this.filteredProducts = (this.category) ?
          this.products.filter(prod => prod.category === this.category) :
          this.products;
      });
  }
 async ngOnInit() {
   this.subcription = (await this.shoppingCartService
     .getCartItems()).subscribe(cart => this.cart = cart);
   
  }

  ngOnDestroy() {
    this.subcription.unsubscribe();
  }
}

【问题讨论】:

标签: angular google-cloud-firestore service null subscription


【解决方案1】:

在帖子的指导下:stackoverflow.com/questions/39627396 这是我目前的工作。我确信有更好的方法,但是在我等待额外的输入时这已经足够了:

export class ShoppingCartService {
  shoppingCart$!: AngularFirestoreCollection<ShoppingCart>;
  constructor(private afs: AngularFirestore) {
          this.shoppingCart$ = <AngularFirestoreCollection<ShoppingCart>> this.afs.collection('shopping-carts');
    
    this.initializeShoppingCart();
  }
  
  initializeShoppingCart() {
        let cartId = localStorage.getItem('cartId');
        if (!cartId) {
        
            this.shoppingCart$.add({
              dateCreated: new Date().getTime()
            }).then(result => {
               localStorage.setItem('cartId', result.id)
               console.log('cart intialized: ', cartId)
            });
        }
 }
  subscribeToShoppingCartService(): AngularFirestoreCollection<ShoppingCart> {
    return this.shoppingCart$ as AngularFirestoreCollection<ShoppingCart>;
  }

【讨论】:

    猜你喜欢
    • 2018-10-21
    • 2021-06-05
    • 2021-07-08
    • 1970-01-01
    • 1970-01-01
    • 2021-11-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多