UNPKG

@pega/dx-component-builder-sdk

Version:

Utility for building custom UI components

251 lines (214 loc) 8.36 kB
import { Component, OnInit, Input, ChangeDetectorRef, forwardRef, OnDestroy } from '@angular/core'; import { CommonModule } from '@angular/common'; import { FormControl, FormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms'; import { MatInputModule } from '@angular/material/input'; import { MatFormFieldModule } from '@angular/material/form-field'; import { NgxCurrencyDirective, NgxCurrencyInputMode } from 'ngx-currency'; import { interval } from 'rxjs'; import { AngularPConnectData, AngularPConnectService } from '@pega/angular-sdk-components'; import { Utils } from '@pega/angular-sdk-components'; import { ComponentMapperComponent } from '@pega/angular-sdk-components'; import { handleEvent } from '@pega/angular-sdk-components'; import { getCurrencyCharacters, getCurrencyOptions } from '@pega/angular-sdk-components'; import { PConnFieldProps } from '@pega/angular-sdk-components'; import { format } from '@pega/angular-sdk-components'; interface DecimalProps extends PConnFieldProps { // If any, enter additional props that only exist on Decimal here currencyISOCode?: string; decimalPrecision?: number; showGroupSeparators?: string; formatter?: string; } @Component({ selector: 'app-decimal', templateUrl: './decimal.component.html', styleUrls: ['./decimal.component.scss'], standalone: true, imports: [ CommonModule, ReactiveFormsModule, FormsModule, MatFormFieldModule, MatInputModule, NgxCurrencyDirective, forwardRef(() => ComponentMapperComponent) ] }) export class DecimalComponent implements OnInit, OnDestroy { @Input() pConn$: typeof PConnect; @Input() formGroup$: FormGroup; // Used with AngularPConnect angularPConnectData: AngularPConnectData = {}; configProps$: DecimalProps; label$ = ''; value$: any; bRequired$ = false; bReadonly$ = false; bDisabled$ = false; bVisible$ = true; displayMode$?: string = ''; controlName$: string; bHasForm$ = true; componentReference = ''; testId: string; helperText: string; placeholder: string; fieldControl = new FormControl<number | null>(null, null); decimalSeparator: string; thousandSeparator: string; currencySymbol = ''; decimalPrecision: number | undefined; formatter; formattedValue: any; inputMode: any; suffix = ''; constructor( private angularPConnect: AngularPConnectService, private cdRef: ChangeDetectorRef, private utils: Utils ) {} ngOnInit(): void { // First thing in initialization is registering and subscribing to the AngularPConnect service this.angularPConnectData = this.angularPConnect.registerAndSubscribeComponent(this, this.onStateChange); this.controlName$ = this.angularPConnect.getComponentID(this); // Then, continue on with other initialization // call updateSelf when initializing // this.updateSelf(); this.checkAndUpdate(); if (this.formGroup$) { // add control to formGroup this.formGroup$.addControl(this.controlName$, this.fieldControl); this.fieldControl.setValue(this.value$); this.bHasForm$ = true; } else { this.bReadonly$ = true; this.bHasForm$ = false; } } ngOnDestroy(): void { if (this.formGroup$) { this.formGroup$.removeControl(this.controlName$); } if (this.angularPConnectData.unsubscribeFn) { this.angularPConnectData.unsubscribeFn(); } } // Callback passed when subscribing to store change onStateChange() { this.checkAndUpdate(); } checkAndUpdate() { // Should always check the bridge to see if the component should // update itself (re-render) const bUpdateSelf = this.angularPConnect.shouldComponentUpdate(this); // ONLY call updateSelf when the component should update if (bUpdateSelf) { this.updateSelf(); } } // updateSelf updateSelf(): void { // starting very simple... // moved this from ngOnInit() and call this from there instead... this.configProps$ = this.pConn$.resolveConfigProps(this.pConn$.getConfigProps()) as DecimalProps; this.testId = this.configProps$.testId; this.label$ = this.configProps$.label; this.displayMode$ = this.configProps$.displayMode; this.inputMode = NgxCurrencyInputMode.Natural; let nValue: any = this.configProps$.value; if (nValue) { if (typeof nValue === 'string') { nValue = parseFloat(nValue); } this.value$ = nValue; this.fieldControl.setValue(this.value$); } this.helperText = this.configProps$.helperText; this.placeholder = this.configProps$.placeholder || ''; const showGroupSeparators = this.configProps$.showGroupSeparators; const currencyISOCode = this.configProps$?.currencyISOCode ?? ''; const theSymbols = getCurrencyCharacters(currencyISOCode); this.decimalSeparator = theSymbols.theDecimalIndicator; this.thousandSeparator = showGroupSeparators ? theSymbols.theDigitGroupSeparator : ''; const theCurrencyOptions = getCurrencyOptions(currencyISOCode); this.formatter = this.configProps$.formatter; if (this.formatter) { this.formattedValue = format(this.value$, this.formatter.toLowerCase(), theCurrencyOptions); } else { this.formattedValue = format(this.value$, 'decimal', theCurrencyOptions); } // timeout and detectChanges to avoid ExpressionChangedAfterItHasBeenCheckedError setTimeout(() => { if (this.configProps$.required != null) { this.bRequired$ = this.utils.getBooleanValue(this.configProps$.required); } this.cdRef.detectChanges(); }); if (this.configProps$.visibility != null) { this.bVisible$ = this.utils.getBooleanValue(this.configProps$.visibility); } if (this.configProps$.readOnly != null) { this.bReadonly$ = this.utils.getBooleanValue(this.configProps$.readOnly); } // disabled if (this.configProps$.disabled != undefined) { this.bDisabled$ = this.utils.getBooleanValue(this.configProps$.disabled); } if (this.bDisabled$) { this.fieldControl.disable(); } else { this.fieldControl.enable(); } if (this.bReadonly$ && this.formatter === 'Currency') { this.currencySymbol = theSymbols.theCurrencySymbol; } if (this.bReadonly$ && this.formatter === 'Percentage') { this.suffix = '%'; } this.decimalPrecision = this.configProps$?.decimalPrecision ?? 2; this.componentReference = this.pConn$.getStateProps().value; // trigger display of error message with field control if (this.angularPConnectData.validateMessage != null && this.angularPConnectData.validateMessage != '') { const timer = interval(100).subscribe(() => { this.fieldControl.setErrors({ message: true }); this.fieldControl.markAsTouched(); timer.unsubscribe(); }); } } fieldOnBlur(event: any) { const oldVal = this.value$ ?? ''; const isValueChanged = event.target.value.toString() !== oldVal.toString(); if (isValueChanged) { const actionsApi = this.pConn$?.getActionsApi(); const propName = this.pConn$?.getStateProps().value; let value = event?.target?.value; // replacing thousand separator with empty string as not required in api call if (this.configProps$.showGroupSeparators) { const thousandSep = this.thousandSeparator === '.' ? '\\.' : this.thousandSeparator; const regExp = new RegExp(String.raw`${thousandSep}`, 'g'); value = value?.replace(regExp, ''); } // replacing decimal separator with '.' if (this.decimalSeparator !== '.') { const regExp = new RegExp(String.raw`${this.decimalSeparator}`, 'g'); value = value.replace(regExp, '.'); } handleEvent(actionsApi, 'changeNblur', propName, value); } } getErrorMessage() { let errMessage = ''; // look for validation messages for json, pre-defined or just an error pushed from workitem (400) if (this.fieldControl.hasError('message')) { errMessage = this.angularPConnectData.validateMessage ?? ''; return errMessage; } if (this.fieldControl.hasError('required')) { errMessage = 'You must enter a value'; } else if (this.fieldControl.errors) { errMessage = this.fieldControl.errors.toString(); } return errMessage; } }