Проблема с бесконечным циклом с использованием библиотеки ngrx

#javascript #angular #ngrx

#javascript #угловой #ngrx

Вопрос:

cart.service.ts

 import { Tools } from './tools';
import { Cart, cartSelector } from './../_store/state/cart/cart.state';
import {
  AddProduct,
  RemoveProduct,
  AddMultiple,
  CartMsg,
  TypeMessageAlreadyCart,
  TypeEmptyCart,
  AddAdress,
  UpdateProduct
} from './../_store/state/cart/cart.actions';
import { CartItem, Variant } from './../_store/models/cart-item.model';
import { Store } from '@ngrx/store';
import { Injectable, OnDestroy } from '@angular/core';
import { RootState } from '../_store/state/root/root.state';
import { Product } from 'src/app/pages/_model/product.model';
import { Subscription } from 'rxjs';

@Injectable({
  providedIn: 'root',
})
export class CartService implements OnDestroy {
  products: CartItem[];
  cartStoreId = 'cart';
  idAddress = 0;
  subscription = new Subscription();

  constructor(
    private store: Store<RootState>,
    private tools: Tools,
  ) {
    // this.tools.storeInfo({}, this.cartStoreId); // Line to debug Cart
    this.recoverCartInfo();
    const select = this.store.select(cartSelector).subscribe(
      cart => {
        this.products = JSON.parse(JSON.stringify(cart.products));
        this.idAddress = cart.idAddress;
    });
    this.subscription.add(select);
  }


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

  storeCartInfo() {
    const cartStored = {
      products: this.products,
      idAddress: this.idAddress
    };
    this.tools.storeInfo(cartStored, this.cartStoreId);
  }

  updateCartPrices(idProduct: number, variants) {
        let idx = this.tools.findProduct(idProduct, this.products);
        if (idx !== -1) {
            let total = 0;
            this.products[idx].variants.forEach(variant => {
                for (let item of variants) {
                    if (variant.idVariant === item.ID_VARIANT) {
                        variant.unitPrice = item.PRICE;
                        total  = variant.unitPrice * variant.qty;
                        continue;
                    }
                }
            })
            this.products[idx].totalPrice = total;
        }
        //this.store.dispatch(new UpdateProduct(this.products));
        this.storeCartInfo();

  }

  recoverCartInfo() {
    const cart = this.tools.getInfo(this.cartStoreId);
    if (cart amp;amp; (cart.products) amp;amp; (cart.products.length > 0)) {
      const result: Cart = {
        totalProducts: cart.products.length,
        idAddress: cart.idAddress,
        products: cart.products,
        msgType: '',
        currency: this.tools.getInfo('currency_default')
      };
      this.store.dispatch(new AddMultiple(result));
    }
  }

  addProductToCart(product: Product, variants?: Variant[]) {
    if (this.changeProductInCart(product, variants)) {
      this.store.dispatch(new AddProduct(this.products));
      this.storeCartInfo();
      return true;
    } else {
      this.store.dispatch(new TypeMessageAlreadyCart());
      return false;
    }
  }

  changeProductInCart(product: Product, variants?: Variant[]) {
    const index = this.tools.findProduct(product.ID_PRODUCT, this.products);
    const attrAdded = (variants amp;amp; (variants.length > 0)) ? this.mergeVariantsCart(product.ID_PRODUCT, variants) : [];
    const totals = this.counterTotals(attrAdded);
    if (index === - 1) {
      const item = {
        idProduct: product.ID_PRODUCT,
        reference: product.REFERENCE,
        idProductImage: (product.ID_PRODUCT_IMAGE) ? product.ID_PRODUCT_IMAGE : 0,
        name: product.NAME,
        totalQty: totals.tQty,
        totalPrice: totals.tPrice,
        variants: attrAdded
      };
      this.products.push(item);
      return true;
    } else if (variants amp;amp; (variants.length > 0)) {
      this.products[index].variants = attrAdded;
      this.products[index].totalQty = totals.tQty;
      this.products[index].totalPrice = totals.tPrice;
      return true;
    }
    return false;
  }

  mergeVariantsCart(idProduct: number, variants: Variant[]) {
    if (this.products.length > 0) {
      const index = this.tools.findProduct(idProduct, this.products);
      if (index >= 0) {
        const varInCart = this.products[index].variants;
        if (varInCart.length > 0) {
          variants.forEach(variant => {
            const idx = this.tools.findVariant(variant.idVariant, varInCart);
            if (variant.qty > 0) {
              if (idx >= 0) {
                varInCart[idx].qty = variant.qty;
                varInCart[idx].unitPrice = variant.unitPrice;
              } else {
                varInCart.push(variant);
              }
            } else if (idx >= 0) {
              varInCart.splice(idx, 1);
            }
          });
        } else {
          variants.forEach(variant => {
            if (variant.qty > 0) {
              varInCart.push(variant);
            }
          });
        }
        return varInCart;
      } else {
        return variants;
      }
    } else {
      return variants;
    }
  }

  counterTotals(variants: Variant[]) {
    let totalQty = 0;
    let totalPrice = 0;
    if (!this.tools.isArrayEmpty(variants)) {
      variants.forEach(variant => {
        totalQty  = (variant.qty * 1);
        totalPrice  = (variant.qty * variant.unitPrice);
      });
    }
    return {
      tQty : totalQty,
      tPrice: totalPrice
    };
  }

  addMultipleProductsToCart(products: any[]) {
    let added = false;
    products.forEach(p => {
      if (this.changeProductInCart(p.product, p.variants)) {
        added = true;
      }
    });
    if (added) {
      const result: Cart = {
        totalProducts: this.products.length,
        idAddress: this.idAddress,
        products: this.products,
        msgType: CartMsg.msgOk,
        currency: this.tools.getInfo('currency_default')
      };
      this.store.dispatch(new AddMultiple(result));
      this.storeCartInfo();
      return true;
    } else {
      this.store.dispatch(new TypeMessageAlreadyCart());
    }
    return false;
  }

  removeProductFromCart(id: number) {
    if (this.products.length > 0) {
      const index = this.tools.findProduct(id, this.products);
      if (index >= 0) {
        this.products.splice(index, 1);
        this.store.dispatch(new RemoveProduct(this.products));
        this.storeCartInfo();
        return true;
      }
    } else {
      this.store.dispatch(new TypeEmptyCart());
    }
    return false;
  }

  destroyCart() {
    this.products = [];
    const cart: Cart = {
      totalProducts: 0,
      idAddress: 0,
      products: this.products,
      msgType: '',
      currency: this.tools.getInfo('currency_default')
    };
    this.store.dispatch(new AddMultiple(cart));
    this.storeCartInfo();
  }

  addAddress(idAddress: number) {
    this.idAddress = idAddress;
    this.store.dispatch(new AddAdress(idAddress));
    this.storeCartInfo();
  }
}
  

variant-selector.ts

 import { Tools } from './../../../../core/_services/tools';
import { CONFIG } from './../../../../app.config';
import {
    Component,
    Input,
    Output,
    EventEmitter,
    Renderer2,
    ElementRef,
    ViewChild,
    OnChanges,
    AfterViewInit,
    OnDestroy
} from '@angular/core';
import { Variant } from 'src/app/core/_store/models/cart-item.model';
import { ResponsiveService } from 'src/app/core/_services/resposive.service';
import { Subscription, Observable } from 'rxjs';
import { Store } from '@ngrx/store';
import { RootState } from 'src/app/core/_store/state/root/root.state';
import {
    hidePricesSelector,
    hideNoStockSelector
} from 'src/app/core/_store/state/config/config.state';
import { UpdateProduct } from 'src/app/core/_store/state/cart/cart.actions';
import { CartService } from 'src/app/core/_services/cart.service';

@Component({
    selector: 'b2b-variants-selector',
    templateUrl: './variants-selector.component.html'
})
export class VariantsSelectorComponent
    implements OnChanges, AfterViewInit, OnDestroy {
    @Input() idProduct: number;
    @Input() variants: any[];
    @Input() variantsInCart: Variant[];
    @Input() attributes: any[];
    @Input() reference: string;
    @Input() isCheckout: boolean;
    @Output() variantChange = new EventEmitter<Variant>();
    @Output() getValue = new EventEmitter<any>();

    @ViewChild('attTable') attTable: ElementRef;
    @ViewChild('variantTable') variantTable: ElementRef;

    showMatrix = CONFIG.showMatrix;
    pricesBySize = CONFIG.pricesBySize;
    colors: any;
    sizes: any;
    tableRealWidth = 0;
    tableRotate = 0;
    hideArrows = false;
    variantSelected: any;
    groupSelection: any[] = [];
    imgPrd = CONFIG.productImg;
    subscription = new Subscription();
    private hidePrices$: Observable<boolean>;
    private hideNoStock$: Observable<boolean>;

    constructor(
        private tools: Tools,
        private renderer: Renderer2,
        private responsiveService: ResponsiveService,
        private store: Store<RootState>,
        private cartService: CartService
    ) {
        const getRes = this.responsiveService
            .getResponsiveDeviceStatus$()
            .subscribe(() => this.checkVisibility());
        this.subscription.add(getRes);
        this.hidePrices$ = this.store.select(hidePricesSelector);
        this.hideNoStock$ = this.store.select(hideNoStockSelector);
    }

    ngOnInit() {
            this.cartService.updateCartPrices(this.idProduct,this.variants);        
    }

    ngOnChanges() {
        if (this.showMatrix amp;amp; this.attributes.length !== 0) {
            this.calculateMatrixAttrs();
        }
    }

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

    ngAfterViewInit() {
        setTimeout(() => {
            this.checkVisibility();
        }, 10);
    }

    checkColourOutofStock(color) {
        for (const size of this.sizes.ATTR_VALUES) {
            const variant = this.getVariant(
                this.colors.ID_ATTRIBUTE_GROUP  
                '-'  
                color.ID_ATTRIBUTE  
                '-'  
                this.sizes.ID_ATTRIBUTE_GROUP  
                '-'  
                size.ID_ATTRIBUTE
            );
            if (
                variant amp;amp;
                (variant.OUT_OF_STOCK === true ||
                    variant.QUANTITY > 0 ||
                    variant.QUANTITY_PRODUCTION > 0)
            ) {
                return false;
            }
        }
        return true;
    }

    calculateMatrixAttrs() {
        this.attributes.forEach((attrGroup: any) => {
            if (attrGroup.IS_COLOR_GROUP) {
                for (const item of attrGroup.ATTR_VALUES) {
                    item.ACTIVE = false;
                }
                this.colors = attrGroup;
            } else {
                this.sizes = attrGroup;
                this.tableRealWidth =
                    this.tools.getObjectLength(this.sizes.ATTR_VALUES) *
                    (this.pricesBySize ? 75 : 115)  
                    45;
            }
        });
    }

    getMatrixPrice(attrIds: string) {
        const variant = this.getVariant(attrIds);
        return variant.PRICE;
    }

    getMatrixDiscount(attrIds: string) {
        const variant = this.getVariant(attrIds);
        return variant.DISCOUNT ? variant.DISCOUNT : null;
    }

    getMatrixDiscountType(attrIds: string) {
        const variant = this.getVariant(attrIds);
        return variant.DISCOUNT_TYPE ? variant.DISCOUNT_TYPE : null;
    }

    checkVisibility() {
        const visibleWidth = this.attTable.nativeElement.offsetWidth - 80;
        if (visibleWidth > this.tableRealWidth) {
            this.hideArrows = true;
        } else {
            this.hideArrows = false;
        }
    }

    rotateTable(direction: string) {
        const visibleWidth = this.attTable.nativeElement.offsetWidth - 95;
        if (visibleWidth < this.tableRealWidth) {
            let move = this.tableRotate;
            if (direction === 'left') {
                move  = 240;
                if (move > 0) {
                    move = 0;
                }
            } else {
                const offsetRigth = visibleWidth - this.tableRealWidth;
                move -= 240;
                if (move < offsetRigth) {
                    move = offsetRigth;
                }
            }
            this.tableRotate = move;
            this.renderer.setStyle(
                this.variantTable.nativeElement,
                'transform',
                'translate3d('   this.tableRotate   'px, 0px, 0px)'
            );
        }
    }

    findGroup(id: number) {
        if (!this.tools.isArrayEmpty(this.groupSelection)) {
            for (let i = 0; i < this.groupSelection.length; i  ) {
                if (this.groupSelection[i].idGroup === id) {
                    return i;
                }
            }
        }
        return -1;
    }

    checkSelected(groupId: number, variantId: number) {
        const idx = this.findGroup(groupId);
        if (idx >= 0 amp;amp; this.groupSelection[idx].idVariant === variantId) {
            return true;
        }
        return false;
    }

    changeVariantSelection(groupId: number, variantId: number) {
        const idx = this.findGroup(groupId);
        if (idx === -1) {
            this.groupSelection.push({
                idGroup: groupId,
                idVariant: variantId
            });
        } else {
            this.groupSelection[idx].idVariant = variantId;
        }
        this.getVariantSelected();
    }

    getVariantSelected() {
        const sortGroup = this.groupSelection.sort((a, b) =>
            a.idGroup > b.idGroup ? 1 : -1
        );
        let hassIds = '';
        let counter = 0;
        sortGroup.forEach(g => {
            if (counter > 0) {
                hassIds  = '-';
            }
            hassIds  = g.idGroup   '-'   g.idVariant;
            counter  ;
        });
        this.variantSelected = this.getVariant(hassIds);
    }

    sendVariantAdded(variant: Variant) {
        this.variantChange.next(variant);
    }

    getQtyincart(idVariant: number) {
        const idx = this.tools.findVariant(idVariant, this.variantsInCart);
        return idx >= 0 ? this.variantsInCart[idx].qty : 0;
    }

    getVariant(attrIds: string) {
        let variantData = null;
        this.variants.forEach(variant => {
            if (variant.ATTRIDS === attrIds) {
                variantData = { ...variant };
                variantData.qtyIncArt = this.getQtyincart(variantData.ID_VARIANT);
            }
        });
        return variantData;
    }

    changeImage(color, active) {
        const tempActive = active;
        let init = false;
        for (const item of this.colors.ATTR_VALUES) {
            item.ACTIVE = false;
        }
        if (this.colors.ATTR_VALUES.find(x => x.ATTR_REFERENCE === color)) {
            if (tempActive) {
                this.colors.ATTR_VALUES.find(
                    x => x.ATTR_REFERENCE === color
                ).ACTIVE = false;
                init = true;
            } else if (
                this.colors.ATTR_VALUES.find(x => x.ATTR_REFERENCE === color)
                    .IMAGES[0] !== ''
            ) {
                this.colors.ATTR_VALUES.find(
                    x => x.ATTR_REFERENCE === color
                ).ACTIVE = true;
            }
        }
        this.getValue.emit({ color, init });
    }
}

  

В приведенном выше коде я пытаюсь вызвать функцию updateCartPrices , но она переходит в бесконечный цикл из-за отправки комментариев. Я новичок в angular, и было бы здорово, если бы вы помогли мне решить эту проблему

Я думаю, проблема в том, что при обновлении состояния происходит перезагрузка представления, я не понимаю, почему