File

projects/storefrontlib/cms-components/product/product-tabs/product-reviews/product-reviews.component.ts

Metadata

changeDetection ChangeDetectionStrategy.OnPush
selector cx-product-reviews
templateUrl ./product-reviews.component.html

Index

Properties
Methods

Constructor

constructor(reviewService: ProductReviewService, currentProductService: CurrentProductService, fb: FormBuilder, cd: ChangeDetectorRef)
Parameters :
Name Type Optional
reviewService ProductReviewService No
currentProductService CurrentProductService No
fb FormBuilder No
cd ChangeDetectorRef No

Methods

addReview
addReview(product: Product)
Parameters :
Name Type Optional
product Product No
Returns : void
cancelWriteReview
cancelWriteReview()
Returns : void
initiateWriteReview
initiateWriteReview()
Returns : void
Private resetReviewForm
resetReviewForm()
Returns : void
setRating
setRating(rating: number)
Parameters :
Name Type Optional
rating number No
Returns : void
submitReview
submitReview(product: Product)
Parameters :
Name Type Optional
product Product No
Returns : void

Properties

initialMaxListItems
Type : number
Default value : 5
isWritingReview
Default value : false
maxListItems
Type : number
product$
Type : Observable<Product>
Default value : this.currentProductService.getProduct()
reviewForm
Type : FormGroup
reviews$
Type : Observable<Review[]>
Default value : this.product$.pipe( filter((p) => !!p), map((p) => p.code), distinctUntilChanged(), switchMap((productCode) => this.reviewService.getByProductCode(productCode) ), tap(() => { this.resetReviewForm(); this.maxListItems = this.initialMaxListItems; }) )
titleInput
Type : ElementRef
Decorators :
@ViewChild('titleInput', {static: false})
writeReviewButton
Type : ElementRef
Decorators :
@ViewChild('writeReviewButton', {static: false})
import {
  ChangeDetectionStrategy,
  Component,
  ViewChild,
  ElementRef,
  ChangeDetectorRef,
} from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Product, ProductReviewService, Review } from '@spartacus/core';
import { Observable } from 'rxjs';
import {
  distinctUntilChanged,
  filter,
  map,
  switchMap,
  tap,
} from 'rxjs/operators';
import { CurrentProductService } from '../../current-product.service';
import { CustomFormValidators } from '../../../../shared/index';

@Component({
  selector: 'cx-product-reviews',
  templateUrl: './product-reviews.component.html',
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ProductReviewsComponent {
  @ViewChild('titleInput', { static: false }) titleInput: ElementRef;
  @ViewChild('writeReviewButton', { static: false })
  writeReviewButton: ElementRef;

  isWritingReview = false;

  // TODO: configurable
  initialMaxListItems = 5;
  maxListItems: number;
  reviewForm: FormGroup;

  product$: Observable<Product> = this.currentProductService.getProduct();

  reviews$: Observable<Review[]> = this.product$.pipe(
    filter((p) => !!p),
    map((p) => p.code),
    distinctUntilChanged(),
    switchMap((productCode) =>
      this.reviewService.getByProductCode(productCode)
    ),
    tap(() => {
      this.resetReviewForm();
      this.maxListItems = this.initialMaxListItems;
    })
  );

  constructor(
    protected reviewService: ProductReviewService,
    protected currentProductService: CurrentProductService,
    private fb: FormBuilder,
    protected cd: ChangeDetectorRef
  ) {}

  initiateWriteReview(): void {
    this.isWritingReview = true;

    this.cd.detectChanges();

    if (this.titleInput && this.titleInput.nativeElement) {
      this.titleInput.nativeElement.focus();
    }
  }

  cancelWriteReview(): void {
    this.isWritingReview = false;
    this.resetReviewForm();

    this.cd.detectChanges();

    if (this.writeReviewButton && this.writeReviewButton.nativeElement) {
      this.writeReviewButton.nativeElement.focus();
    }
  }

  setRating(rating: number): void {
    this.reviewForm.controls.rating.setValue(rating);
  }

  submitReview(product: Product) {
    if (this.reviewForm.valid) {
      this.addReview(product);
    } else {
      this.reviewForm.markAllAsTouched();
    }
  }

  addReview(product: Product): void {
    const reviewFormControls = this.reviewForm.controls;
    const review: Review = {
      headline: reviewFormControls.title.value,
      comment: reviewFormControls.comment.value,
      rating: reviewFormControls.rating.value,
      alias: reviewFormControls.reviewerName.value,
    };

    this.reviewService.add(product.code, review);

    this.isWritingReview = false;
    this.resetReviewForm();

    this.cd.detectChanges();

    if (this.writeReviewButton && this.writeReviewButton.nativeElement) {
      this.writeReviewButton.nativeElement.focus();
    }
  }

  private resetReviewForm(): void {
    this.reviewForm = this.fb.group({
      title: ['', Validators.required],
      comment: ['', Validators.required],
      rating: [null, CustomFormValidators.starRatingEmpty],
      reviewerName: '',
    });
  }
}
<div class="container" *ngIf="product$ | async as product">
  <h2>
    {{ 'productDetails.reviews' | cxTranslate }} ({{ product.numberOfReviews }})
  </h2>
  <ng-container *ngIf="!isWritingReview; else writeReview">
    <div class="header">
      <h3>{{ 'productReview.overallRating' | cxTranslate }}</h3>
      <button
        #writeReviewButton
        class="btn btn-primary"
        (click)="initiateWriteReview()"
      >
        {{ 'productReview.writeReview' | cxTranslate }}
      </button>
      <cx-star-rating
        *ngIf="product.averageRating"
        class="rating"
        [rating]="product.averageRating"
      ></cx-star-rating>
      <div class="rating" *ngIf="!product.averageRating">
        {{ 'productDetails.noReviews' | cxTranslate }}
      </div>
    </div>

    <ng-container *ngIf="!isWritingReview; else writeReview">
      <ng-container *ngIf="reviews$ | async as reviews">
        <div
          class="review"
          tabindex="0"
          *ngFor="let review of reviews | slice: 0:maxListItems"
        >
          <div class="title">{{ review.headline }}</div>
          <cx-star-rating [rating]="review.rating"></cx-star-rating>
          <div class="name">
            {{ review.alias ? review.alias : review.principal?.name }}
          </div>
          <div class="date">{{ review.date | cxDate }}</div>
          <div class="text">{{ review.comment }}</div>
        </div>
        <div *ngIf="reviews.length > initialMaxListItems">
          <button
            class="btn btn-primary"
            (click)="maxListItems = reviews.length"
            *ngIf="maxListItems === initialMaxListItems"
          >
            {{ 'productReview.more' | cxTranslate }}
          </button>
          <button
            class="btn btn-primary"
            (click)="maxListItems = initialMaxListItems"
            *ngIf="maxListItems !== initialMaxListItems"
          >
            {{ 'productReview.less' | cxTranslate }}
          </button>
        </div>
      </ng-container>
    </ng-container>
  </ng-container>

  <ng-template #writeReview>
    <form (ngSubmit)="submitReview(product)" [formGroup]="reviewForm">
      <div class="form-group">
        <label>
          <span class="label-content">{{
            'productReview.reviewTitle' | cxTranslate
          }}</span>
          <input
            aria-required="true"
            #titleInput
            type="text"
            class="form-control"
            formControlName="title"
          />
          <cx-form-errors
            aria-live="assertive"
            aria-atomic="true"
            [control]="reviewForm.get('title')"
          ></cx-form-errors>
        </label>
      </div>
      <div class="form-group">
        <label>
          <span class="label-content">{{
            'productReview.writeYourComments' | cxTranslate
          }}</span>
          <textarea
            aria-required="true"
            class="form-control"
            rows="3"
            formControlName="comment"
          ></textarea>
          <cx-form-errors
            aria-live="assertive"
            aria-atomic="true"
            [control]="reviewForm.get('comment')"
          ></cx-form-errors>
        </label>
      </div>
      <div class="form-group">
        <label>
          <span class="label-content">{{
            'productReview.rating' | cxTranslate
          }}</span>
          <input
            aria-required="true"
            type="number"
            formControlName="rating"
            class="rating-input"
          />
          <cx-star-rating
            (change)="setRating($event)"
            [disabled]="false"
          ></cx-star-rating>
          <cx-form-errors
            aria-live="assertive"
            aria-atomic="true"
            [control]="reviewForm.get('rating')"
          ></cx-form-errors>
        </label>
      </div>
      <div class="form-group">
        <label>
          <span class="label-content">{{
            'productReview.reviewerName' | cxTranslate
          }}</span>
          <input
            type="text"
            class="form-control"
            formControlName="reviewerName"
          />
        </label>
      </div>
      <div class="form-group row">
        <div class="col-12 col-md-4">
          <button
            type="button"
            class="btn btn-block btn-secondary"
            (click)="cancelWriteReview()"
          >
            {{ 'common.cancel' | cxTranslate }}
          </button>
        </div>
        <div class="col-12 col-md-4">
          <button type="submit" class="btn btn-block btn-primary">
            {{ 'common.submit' | cxTranslate }}
          </button>
        </div>
      </div>
    </form>
  </ng-template>
</div>
Legend
Html element
Component
Html element with directive

result-matching ""

    No results matching ""