feature-libs/cart/saved-cart/components/saved-cart-form-dialog/saved-cart-form-dialog.component.ts
| changeDetection | ChangeDetectionStrategy.OnPush |
| selector | cx-saved-cart-form-dialog |
| templateUrl | ./saved-cart-form-dialog.component.html |
Properties |
Methods |
|
HostListeners |
Accessors |
constructor(launchDialogService: LaunchDialogService, el: ElementRef, savedCartService: SavedCartFacade, eventService: EventService, routingService: RoutingService, globalMessageService: GlobalMessageService)
|
|||||||||||||||||||||
|
Parameters :
|
| click |
Arguments : '$event'
|
click(event: UIEvent)
|
| Protected build | ||||||
build(cart?: Cart)
|
||||||
|
Parameters :
Returns :
void
|
| close | ||||||
close(reason: string)
|
||||||
|
Parameters :
Returns :
void
|
| deleteCart | ||||||
deleteCart(cartId: string)
|
||||||
|
Parameters :
Returns :
void
|
| handleClick | ||||||
handleClick(event: UIEvent)
|
||||||
Decorators :
@HostListener('click', ['$event'])
|
||||||
|
Parameters :
Returns :
void
|
| ngOnDestroy |
ngOnDestroy()
|
|
Returns :
void
|
| ngOnInit |
ngOnInit()
|
|
Returns :
void
|
| onComplete | ||||||
onComplete(success: boolean)
|
||||||
|
Parameters :
Returns :
void
|
| Protected patchData | ||||||
patchData(item?: any)
|
||||||
|
Parameters :
Returns :
void
|
| Private resetSavedCartStates |
resetSavedCartStates()
|
|
Returns :
void
|
| restoreSavedCart | ||||||
restoreSavedCart(cartId: string)
|
||||||
|
Parameters :
Returns :
void
|
| saveOrEditCart | ||||||
saveOrEditCart(cartId: string)
|
||||||
|
Parameters :
Returns :
void
|
| toggleIsCloneSavedCart |
toggleIsCloneSavedCart()
|
|
Returns :
boolean
|
| cart |
Type : Cart
|
| descriptionMaxLength |
Type : number
|
Default value : 250
|
| focusConfig |
Type : FocusConfig
|
Default value : {
trap: true,
block: true,
autofocus: 'button',
focusOnEscape: true,
}
|
| form |
Type : FormGroup
|
| iconTypes |
Default value : ICON_TYPE
|
| isCloneSavedCart |
Default value : false
|
| isDisableDeleteButton$ |
Type : Observable<boolean>
|
| isDisableRestoreButton$ |
Type : Observable<boolean>
|
| isLoading$ |
Type : Observable<boolean>
|
| layoutOption |
Type : string | undefined
|
| nameMaxLength |
Type : number
|
Default value : 50
|
| savedCartFormType |
Default value : SavedCartFormType
|
| Private subscription |
Default value : new Subscription()
|
| descriptionsCharacterLeft |
getdescriptionsCharacterLeft()
|
import {
ChangeDetectionStrategy,
Component,
ElementRef,
HostListener,
OnDestroy,
OnInit,
} from '@angular/core';
import { FormControl, FormGroup, Validators } from '@angular/forms';
import {
DeleteSavedCartEvent,
DeleteSavedCartFailEvent,
DeleteSavedCartSuccessEvent,
SavedCartFacade,
SavedCartFormType,
} from '@spartacus/cart/saved-cart/root';
import {
Cart,
EventService,
GlobalMessageService,
GlobalMessageType,
RoutingService,
} from '@spartacus/core';
import {
FocusConfig,
ICON_TYPE,
LaunchDialogService,
} from '@spartacus/storefront';
import { combineLatest, merge, Observable, Subscription } from 'rxjs';
import { map, mapTo, take } from 'rxjs/operators';
export interface SavedCartFormDialogOptions {
cart: Cart;
layoutOption?: string;
}
@Component({
selector: 'cx-saved-cart-form-dialog',
templateUrl: './saved-cart-form-dialog.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class SavedCartFormDialogComponent implements OnInit, OnDestroy {
private subscription = new Subscription();
savedCartFormType = SavedCartFormType;
form: FormGroup;
iconTypes = ICON_TYPE;
cart: Cart;
layoutOption: string | undefined;
descriptionMaxLength: number = 250;
nameMaxLength: number = 50;
isCloneSavedCart = false;
focusConfig: FocusConfig = {
trap: true,
block: true,
autofocus: 'button',
focusOnEscape: true,
};
isLoading$: Observable<boolean>;
isDisableDeleteButton$: Observable<boolean>;
isDisableRestoreButton$: Observable<boolean>;
get descriptionsCharacterLeft(): number {
return (
this.descriptionMaxLength -
(this.form.get('description')?.value?.length || 0)
);
}
@HostListener('click', ['$event'])
handleClick(event: UIEvent): void {
// Close on click outside the dialog window
if ((event.target as any).tagName === this.el.nativeElement.tagName) {
this.close('Cross click');
}
}
constructor(
protected launchDialogService: LaunchDialogService,
protected el: ElementRef,
protected savedCartService: SavedCartFacade,
protected eventService: EventService,
protected routingService: RoutingService,
protected globalMessageService: GlobalMessageService
) {}
ngOnInit(): void {
this.resetSavedCartStates();
this.isLoading$ = this.savedCartService.getSaveCartProcessLoading();
this.isDisableDeleteButton$ = merge(
this.eventService.get(DeleteSavedCartEvent).pipe(take(1), mapTo(true)),
this.eventService
.get(DeleteSavedCartFailEvent)
.pipe(take(1), mapTo(false))
);
this.isDisableRestoreButton$ = combineLatest([
this.savedCartService.getCloneSavedCartProcessLoading(),
this.savedCartService.getRestoreSavedCartProcessLoading(),
]).pipe(
map(
([isCloneLoading, isRestoreLoading]) =>
isCloneLoading || isRestoreLoading
)
);
this.subscription.add(
this.launchDialogService.data$.subscribe(
(data: SavedCartFormDialogOptions) => {
this.cart = data.cart;
this.layoutOption = data.layoutOption;
this.build(this.cart);
}
)
);
this.subscription.add(
this.savedCartService
.getSaveCartProcessSuccess()
.subscribe((success) => this.onComplete(success))
);
this.subscription.add(
this.eventService
.get(DeleteSavedCartSuccessEvent)
.pipe(take(1), mapTo(true))
.subscribe((success) => this.onComplete(success))
);
this.subscription.add(
this.savedCartService
.getRestoreSavedCartProcessSuccess()
.subscribe((success) => this.onComplete(success))
);
}
saveOrEditCart(cartId: string): void {
const name = this.form.get('name')?.value;
// TODO(#12660): Remove default value once backend is updated
const description = this.form.get('description')?.value || '-';
switch (this.layoutOption) {
case SavedCartFormType.SAVE: {
this.savedCartService.saveCart({
cartId,
saveCartName: name,
saveCartDescription: description,
});
break;
}
case SavedCartFormType.EDIT: {
this.savedCartService.editSavedCart({
cartId,
saveCartName: name,
saveCartDescription: description,
});
break;
}
}
}
deleteCart(cartId: string): void {
this.savedCartService.deleteSavedCart(cartId);
}
restoreSavedCart(cartId: string): void {
if (this.isCloneSavedCart) {
this.savedCartService.cloneSavedCart(
cartId,
this.form.get('cloneName')?.value
);
} else {
this.savedCartService.restoreSavedCart(cartId);
}
}
close(reason: string): void {
this.launchDialogService.closeDialog(reason);
}
onComplete(success: boolean): void {
if (success) {
switch (this.layoutOption) {
case SavedCartFormType.DELETE: {
this.routingService.go({ cxRoute: 'savedCarts' });
this.globalMessageService.add(
{
key: 'savedCartDialog.deleteCartSuccess',
},
GlobalMessageType.MSG_TYPE_CONFIRMATION
);
this.close('Successfully deleted a saved cart');
break;
}
case SavedCartFormType.SAVE: {
this.close('Successfully saved cart');
this.savedCartService.clearSaveCart();
this.globalMessageService.add(
{
key: 'savedCartCartPage.messages.cartSaved',
params: {
cartName: this.form.get('name')?.value || this.cart?.code,
},
},
GlobalMessageType.MSG_TYPE_CONFIRMATION
);
break;
}
case SavedCartFormType.EDIT: {
this.close('Successfully edited saved cart');
this.savedCartService.clearSaveCart();
this.globalMessageService.add(
{
key: 'savedCartDialog.editCartSuccess',
params: {
cartName: this.form.get('name')?.value || this.cart?.code,
},
},
GlobalMessageType.MSG_TYPE_CONFIRMATION
);
break;
}
case SavedCartFormType.RESTORE: {
this.close('Successfully restored saved cart');
this.routingService.go({ cxRoute: 'savedCarts' });
this.resetSavedCartStates();
break;
}
}
}
}
toggleIsCloneSavedCart() {
return (this.isCloneSavedCart = !this.isCloneSavedCart);
}
protected build(cart?: Cart) {
const form = new FormGroup({});
form.setControl(
'name',
new FormControl('', [
Validators.required,
Validators.maxLength(this.nameMaxLength),
])
);
form.setControl(
'description',
new FormControl('', [Validators.maxLength(this.descriptionMaxLength)])
);
form.setControl('isCloneSavedCart', new FormControl(''));
form.setControl('cloneName', new FormControl(''));
this.form = form;
this.patchData(cart);
}
protected patchData(item?: any): void {
this.form.patchValue({ ...item });
}
private resetSavedCartStates(): void {
this.savedCartService.clearCloneSavedCart();
this.savedCartService.clearSaveCart();
this.savedCartService.clearRestoreSavedCart();
}
ngOnDestroy(): void {
this.subscription?.unsubscribe();
this.close('close dialog');
}
}
<ng-container *ngIf="cart">
<div
[cxFocus]="focusConfig"
(esc)="close('Escape clicked')"
class="cx-saved-cart-form-dialog"
>
<form [formGroup]="form" class="cx-saved-cart-form-container">
<!-- Modal Header -->
<div class="modal-header cx-saved-cart-form-header">
<ng-container [ngSwitch]="layoutOption">
<div class="cx-saved-cart-form-title modal-title">
<ng-container *ngSwitchCase="savedCartFormType.EDIT">
{{ 'savedCartDialog.editSavedCart' | cxTranslate }}
</ng-container>
<ng-container *ngSwitchCase="savedCartFormType.DELETE">
{{ 'savedCartDialog.deleteSavedCart' | cxTranslate }}
</ng-container>
<ng-container *ngSwitchCase="savedCartFormType.SAVE">
{{ 'savedCartDialog.saveForLater' | cxTranslate }}
</ng-container>
<ng-container *ngSwitchCase="savedCartFormType.RESTORE">
{{ 'savedCartDialog.restoreSavedCart' | cxTranslate }}
</ng-container>
</div>
</ng-container>
<button
(click)="close('Close Save Cart Dialog')"
[disabled]="isLoading$ | async"
[attr.aria-label]="'common.close' | cxTranslate"
class="cx-saved-cart-form-close close"
type="button"
>
<span aria-hidden="true">
<cx-icon [type]="iconTypes.CLOSE"></cx-icon>
</span>
</button>
</div>
<!-- Modal Body -->
<div class="cx-saved-cart-form-body">
<!-- start DELETE and RESTORE form -->
<ng-container
*ngIf="
layoutOption === savedCartFormType.DELETE ||
layoutOption === savedCartFormType.RESTORE;
else saveAndEditCart
"
>
<p class="cx-saved-cart-form-subtitle">
{{
(layoutOption === savedCartFormType.DELETE
? 'savedCartDialog.followingCartDelete'
: 'savedCartDialog.followingCartRestore'
) | cxTranslate
}}
</p>
<div class="cx-saved-cart-form-row">
<div class="cx-saved-cart-values-container">
<div class="cx-saved-cart-label">
{{ 'savedCartDialog.name' | cxTranslate }}
</div>
<div class="cx-saved-cart-value">
{{ cart?.name }}
</div>
</div>
<div class="cx-saved-cart-values-container">
<div class="cx-saved-cart-label">
{{ 'savedCartDialog.id' | cxTranslate }}
</div>
<div class="cx-saved-cart-value">
{{ cart?.code }}
</div>
</div>
<div class="cx-saved-cart-values-container">
<div class="cx-saved-cart-label">
{{ 'savedCartDialog.description' | cxTranslate }}
</div>
<div class="cx-saved-cart-value">
{{ cart?.description }}
</div>
</div>
<div class="cx-saved-cart-values-container">
<div class="cx-saved-cart-label">
{{ 'savedCartDialog.quantity' | cxTranslate }}
</div>
<div class="cx-saved-cart-value">
{{ cart?.totalItems }}
</div>
</div>
<div class="cx-saved-cart-values-container">
<div class="cx-saved-cart-label">
{{ 'savedCartDialog.total' | cxTranslate }}
</div>
<div class="cx-saved-cart-value">
{{ cart?.totalPriceWithTax?.formattedValue }}
</div>
</div>
</div>
<ng-container *ngIf="layoutOption === savedCartFormType.RESTORE">
<div class="cx-copy-saved-cart-row form-check">
<input
id="cx-copy-saved-cart"
type="checkbox"
class="cx-copy-saved-cart-input"
[checked]="isCloneSavedCart"
(change)="toggleIsCloneSavedCart()"
/>
<label
for="cx-copy-saved-cart"
class="cx-copy-saved-cart-label"
>{{ 'savedCartDialog.keepCopySavedCart' | cxTranslate }}</label
>
</div>
<div *ngIf="isCloneSavedCart" class="cx-copy-saved-cart-row">
<label>
<span class="label-content">
{{ 'savedCartDialog.nameOfCloneCart' | cxTranslate }}
</span>
<input
[maxLength]="nameMaxLength"
class="form-control"
formControlName="cloneName"
type="text"
placeholder="{{
'savedCartDialog.defaultCloneCartName'
| cxTranslate: { name: form.get('name')?.value }
}}"
/>
</label>
</div>
</ng-container>
<div class="cx-saved-cart-form-footer">
<button
(click)="close('Close Save Cart Dialog')"
[attr.aria-label]="'common.close' | cxTranslate"
class="mr-2 btn btn-action"
type="button"
>
{{ 'savedCartDialog.cancel' | cxTranslate }}
</button>
<ng-container
*ngIf="
layoutOption === savedCartFormType.DELETE;
else isRestoreSavedCart
"
>
<button
*ngIf="cart.code"
(click)="deleteCart(cart.code)"
[attr.aria-label]="'common.delete' | cxTranslate"
[disabled]="isDisableDeleteButton$ | async"
class="ml-2 btn btn-primary"
type="button"
>
{{ 'savedCartDialog.delete' | cxTranslate }}
</button>
</ng-container>
<ng-template #isRestoreSavedCart>
<button
*ngIf="cart.code"
(click)="restoreSavedCart(cart.code)"
[disabled]="isDisableRestoreButton$ | async"
[attr.aria-label]="'common.restore' | cxTranslate"
class="ml-2 btn btn-primary"
type="button"
>
{{ 'savedCartDialog.restore' | cxTranslate }}
</button>
</ng-template>
</div>
</ng-container>
<!-- end DELETE form -->
<!-- start SAVE and EDIT form -->
<ng-template #saveAndEditCart>
<ng-container *ngIf="layoutOption === savedCartFormType.SAVE">
<p class="cx-saved-cart-form-subtitle">
{{ 'savedCartDialog.itemsSavedForLater' | cxTranslate }}
</p>
</ng-container>
<div class="cx-saved-cart-form-row">
<ng-container>
<label>
<span class="cx-saved-carts-label label-content">{{
'savedCartDialog.savedCartName' | cxTranslate
}}</span>
<input
aria-required="true"
[maxLength]="nameMaxLength"
class="form-control"
formControlName="name"
required
type="text"
/>
<cx-form-errors
aria-live="assertive"
aria-atomic="true"
[control]="form.get('name')"
></cx-form-errors>
</label>
</ng-container>
</div>
<div class="cx-saved-cart-form-row">
<label>
<span class="cx-saved-carts-label label-content"
>{{ 'savedCartDialog.savedCartDescription' | cxTranslate }}
<span class="cx-saved-carts-label-optional">
({{ 'savedCartDialog.optional' | cxTranslate }})
</span></span
>
<textarea
[maxLength]="descriptionMaxLength"
class="form-control"
formControlName="description"
rows="5"
></textarea>
<cx-form-errors
aria-live="assertive"
aria-atomic="true"
[control]="form.get('description')"
></cx-form-errors>
<p class="cx-saved-carts-input-hint">
{{
'savedCartDialog.charactersLeft'
| cxTranslate: { count: descriptionsCharacterLeft }
}}
</p>
</label>
</div>
<div class="cx-saved-cart-form-footer">
<button
(click)="close('Close Save Cart Dialog')"
[attr.aria-label]="'common.close' | cxTranslate"
[disabled]="isLoading$ | async"
class="btn btn-action"
type="button"
>
{{ 'savedCartDialog.cancel' | cxTranslate }}
</button>
<button
(click)="saveOrEditCart(cart?.code)"
[attr.aria-label]="'common.save' | cxTranslate"
[disabled]="form.invalid || (isLoading$ | async)"
class="btn btn-primary"
type="button"
>
{{ 'savedCartDialog.save' | cxTranslate }}
</button>
</div>
</ng-template>
<!-- end SAVE and EDIT form -->
</div>
</form>
</div>
</ng-container>