File

projects/storefrontlib/cms-components/cart/add-to-cart/add-to-cart.component.ts

Implements

OnInit OnDestroy

Metadata

changeDetection ChangeDetectionStrategy.OnPush
selector cx-add-to-cart
templateUrl ./add-to-cart.component.html

Index

Properties
Methods
Inputs

Constructor

constructor(modalService: ModalService, currentProductService: CurrentProductService, cd: ChangeDetectorRef, activeCartService: ActiveCartService, component?: CmsComponentData<CmsAddToCartComponent>)
Parameters :
Name Type Optional
modalService ModalService No
currentProductService CurrentProductService No
cd ChangeDetectorRef No
activeCartService ActiveCartService No
component CmsComponentData<CmsAddToCartComponent> Yes

Inputs

product
Type : Product

As long as we do not support #5026, we require product input, as we need a reference to the product model to fetch the stock data.

productCode
Type : string
showQuantity
Type : boolean
Default value : true

Methods

addToCart
addToCart()
Returns : void
getInventory
getInventory()

In specific scenarios, we need to omit displaying the stock level or append a plus to the value. When backoffice forces a product to be in stock, omit showing the stock level. When product stock level is limited by a threshold value, append '+' at the end. When out of stock, display no numerical value.

Returns : string
ngOnDestroy
ngOnDestroy()
Returns : void
ngOnInit
ngOnInit()
Returns : void
Protected openModal
openModal()

Provides required data and opens AddedToCartDialogComponent modal

Returns : void
Protected setStockInfo
setStockInfo(product: Product)
Parameters :
Name Type Optional
product Product No
Returns : void
updateCount
updateCount(value: number)
Parameters :
Name Type Optional
value number No
Returns : void

Properties

addToCartForm
Default value : new FormGroup({ quantity: new FormControl(1, { updateOn: 'blur' }), })
hasStock
Type : boolean
Default value : false
inventoryThreshold
Type : boolean
Default value : false
maxQuantity
Type : number
modalRef
Type : ModalRef
Protected numberOfEntriesBeforeAdd
Type : number
Default value : 0
product
Type : Product
Decorators :
@Input()

As long as we do not support #5026, we require product input, as we need a reference to the product model to fetch the stock data.

productCode
Type : string
Decorators :
@Input()
quantity
Type : number
Default value : 1
showInventory$
Type : Observable<boolean | undefined> | undefined
Default value : this.component?.data$.pipe(map((data) => data.inventoryDisplay))
showQuantity
Default value : true
Decorators :
@Input()
subscription
Type : Subscription
import {
  ChangeDetectionStrategy,
  ChangeDetectorRef,
  Component,
  Input,
  OnDestroy,
  OnInit,
  Optional,
} from '@angular/core';
import { FormControl, FormGroup } from '@angular/forms';
import {
  ActiveCartService,
  CmsAddToCartComponent,
  isNotNullable,
  Product,
} from '@spartacus/core';
import { Observable, Subscription } from 'rxjs';
import { filter, map, take } from 'rxjs/operators';
import { CmsComponentData } from '../../../cms-structure/page/model/cms-component-data';
import { ModalRef } from '../../../shared/components/modal/modal-ref';
import { ModalService } from '../../../shared/components/modal/modal.service';
import { CurrentProductService } from '../../product/current-product.service';
import { AddedToCartDialogComponent } from './added-to-cart-dialog/added-to-cart-dialog.component';

@Component({
  selector: 'cx-add-to-cart',
  templateUrl: './add-to-cart.component.html',
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AddToCartComponent implements OnInit, OnDestroy {
  @Input() productCode: string;
  @Input() showQuantity = true;

  /**
   * As long as we do not support #5026, we require product input, as we need
   *  a reference to the product model to fetch the stock data.
   */
  @Input() product: Product;

  maxQuantity: number;
  modalRef: ModalRef;

  hasStock: boolean = false;
  inventoryThreshold: boolean = false;

  showInventory$: Observable<boolean | undefined> | undefined =
    this.component?.data$.pipe(map((data) => data.inventoryDisplay));

  quantity = 1;
  protected numberOfEntriesBeforeAdd = 0;

  subscription: Subscription;

  addToCartForm = new FormGroup({
    quantity: new FormControl(1, { updateOn: 'blur' }),
  });

  // TODO(#13041): Remove deprecated constructors
  constructor(
    modalService: ModalService,
    currentProductService: CurrentProductService,
    cd: ChangeDetectorRef,
    activeCartService: ActiveCartService,
    // eslint-disable-next-line @typescript-eslint/unified-signatures
    component?: CmsComponentData<CmsAddToCartComponent>
  );

  /**
   * @deprecated since 4.1
   */
  constructor(
    modalService: ModalService,
    currentProductService: CurrentProductService,
    cd: ChangeDetectorRef,
    activeCartService: ActiveCartService
  );

  constructor(
    protected modalService: ModalService,
    protected currentProductService: CurrentProductService,
    protected cd: ChangeDetectorRef,
    protected activeCartService: ActiveCartService,
    @Optional() protected component?: CmsComponentData<CmsAddToCartComponent>
  ) {}

  ngOnInit() {
    if (this.product) {
      this.productCode = this.product.code ?? '';
      this.setStockInfo(this.product);
      this.cd.markForCheck();
    } else if (this.productCode) {
      // force hasStock and quantity for the time being, as we do not have more info:
      this.quantity = 1;
      this.hasStock = true;
      this.cd.markForCheck();
    } else {
      this.subscription = this.currentProductService
        .getProduct()
        .pipe(filter(isNotNullable))
        .subscribe((product) => {
          this.productCode = product.code ?? '';
          this.setStockInfo(product);
          this.cd.markForCheck();
        });
    }
  }

  protected setStockInfo(product: Product): void {
    this.quantity = 1;
    this.hasStock = Boolean(product.stock?.stockLevelStatus !== 'outOfStock');

    this.inventoryThreshold = product.stock?.isValueRounded ?? false;

    if (this.hasStock && product.stock?.stockLevel) {
      this.maxQuantity = product.stock.stockLevel;
    }
  }

  /**
   * In specific scenarios, we need to omit displaying the stock level or append a plus to the value.
   * When backoffice forces a product to be in stock, omit showing the stock level.
   * When product stock level is limited by a threshold value, append '+' at the end.
   * When out of stock, display no numerical value.
   */
  getInventory(): string {
    if (this.hasStock) {
      const quantityDisplay = this.maxQuantity
        ? this.maxQuantity.toString()
        : '';
      return this.inventoryThreshold ? quantityDisplay + '+' : quantityDisplay;
    } else {
      return '';
    }
  }

  updateCount(value: number): void {
    this.quantity = value;
  }

  addToCart() {
    const quantity = this.addToCartForm.get('quantity').value;
    if (!this.productCode || quantity <= 0) {
      return;
    }
    this.activeCartService
      .getEntries()
      .pipe(take(1))
      .subscribe((entries) => {
        this.numberOfEntriesBeforeAdd = entries.length;
        this.openModal();
        this.activeCartService.addEntry(this.productCode, quantity);
      });
  }

  /**
   * Provides required data and opens AddedToCartDialogComponent modal
   */
  protected openModal() {
    let modalInstance: any;
    this.modalRef = this.modalService.open(AddedToCartDialogComponent, {
      centered: true,
      size: 'lg',
    });

    modalInstance = this.modalRef.componentInstance;
    // Display last entry for new product code. This always corresponds to
    // our new item, independently of whether merging occured or not
    modalInstance.entry$ = this.activeCartService.getLastEntry(
      this.productCode
    );
    modalInstance.cart$ = this.activeCartService.getActive();
    modalInstance.loaded$ = this.activeCartService.isStable();
    modalInstance.quantity = this.quantity;
    modalInstance.numberOfEntriesBeforeAdd = this.numberOfEntriesBeforeAdd;
  }

  ngOnDestroy() {
    if (this.subscription) {
      this.subscription.unsubscribe();
    }
  }
}
<form *ngIf="productCode" [formGroup]="addToCartForm" (submit)="addToCart()">
  <div class="quantity" *ngIf="showQuantity">
    <label>{{ 'addToCart.quantity' | cxTranslate }}</label>
    <cx-item-counter
      *ngIf="hasStock"
      [max]="maxQuantity"
      [control]="addToCartForm.get('quantity')"
    ></cx-item-counter>

    <span class="info">
      <span *ngIf="showInventory$ | async">{{ getInventory() }}</span>
      {{
        hasStock
          ? ('addToCart.inStock' | cxTranslate)
          : ('addToCart.outOfStock' | cxTranslate)
      }}</span
    >
  </div>

  <button
    *ngIf="hasStock"
    class="btn btn-primary btn-block"
    type="submit"
    [disabled]="quantity <= 0 || quantity > maxQuantity"
  >
    {{ 'addToCart.addToCart' | cxTranslate }}
  </button>
</form>
Legend
Html element
Component
Html element with directive

result-matching ""

    No results matching ""