@pega/dx-component-builder-sdk
Version:
Utility for building custom UI components
323 lines (280 loc) • 10 kB
text/typescript
import { Component, OnInit, Input, forwardRef, OnDestroy } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
import { MatAutocompleteModule } from '@angular/material/autocomplete';
import { MatInputModule } from '@angular/material/input';
import { MatOptionModule } from '@angular/material/core';
import { MatSelectModule } from '@angular/material/select';
import { MatFormFieldModule } from '@angular/material/form-field';
import { AngularPConnectData, AngularPConnectService } from '@pega/angular-sdk-components';
import { Utils } from '@pega/angular-sdk-components';
import { ComponentMapperComponent } from '@pega/angular-sdk-components';
import { PConnFieldProps } from '@pega/angular-sdk-components';
import { map, Observable, startWith } from 'rxjs';
import { handleEvent } from '@pega/angular-sdk-components';
const OPERATORS_DP = 'D_pyGetOperatorsForCurrentApplication';
const DROPDOWN_LIST = 'Drop-down list';
const SEARCH_BOX = 'Search box';
interface UserReferenceProps extends Omit<PConnFieldProps, 'value'> {
// If any, enter additional props that only exist on UserReference here
displayAs?: string;
value?: any;
showAsFormattedText?: boolean;
additionalProps?: object;
onRecordChange?: any;
}
export class UserReferenceComponent implements OnInit, OnDestroy {
pConn$: typeof PConnect;
formGroup$: FormGroup;
angularPConnectData: AngularPConnectData = {};
controlName$: string;
value$;
userName$: string;
label$: string;
userID$: string;
options$: any;
bReadonly$: boolean;
bRequired$: boolean;
showAsFormattedText$?: boolean;
displayAs$?: string;
testId: string;
helperText: string;
placeholder: string;
displayMode$?: string;
filteredOptions: Observable<any[]>;
filterValue = '';
fieldControl = new FormControl('', null);
actionsApi: Object;
propName: string;
onRecordChange: any;
constructor(
private angularPConnect: AngularPConnectService,
private utils: Utils
) {}
async ngOnInit(): Promise<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);
await this.checkAndUpdate();
if (this.formGroup$) {
// add control to formGroup
this.formGroup$.addControl(this.controlName$, this.fieldControl);
this.fieldControl.setValue(this.getValue(this.value$));
}
this.filteredOptions = this.fieldControl.valueChanges.pipe(
startWith(this.getValue(this.value$) || ''),
map(value => this._filter(value || ''))
);
}
ngOnDestroy() {
if (this.formGroup$) {
this.formGroup$.removeControl(this.controlName$);
}
if (this.angularPConnectData.unsubscribeFn) {
this.angularPConnectData.unsubscribeFn();
}
}
get type(): string {
if (this.bReadonly$ && this.showAsFormattedText$) {
return 'operator';
}
if (this.displayAs$ === DROPDOWN_LIST) {
return 'dropdown';
}
if (this.displayAs$ === SEARCH_BOX) {
return 'searchbox';
}
return '';
}
// Callback passed when subscribing to store change
async onStateChange() {
await this.checkAndUpdate();
}
private _filter(value: string): string[] {
const filterVal = (value || this.filterValue).toLowerCase();
return this.options$?.filter(option => option.value?.toLowerCase().includes(filterVal));
}
isUserNameAvailable = user => {
return typeof user === 'object' && user !== null && user.userName;
};
getUserName = user => {
return user.userName;
};
getValue = user => {
if (this.displayAs$ === DROPDOWN_LIST) {
return this.utils.getUserId(user) || this.getUserName(user);
}
return this.isUserNameAvailable(user) ? this.getUserName(user) : this.utils.getUserId(user);
};
async 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) {
await this.updateSelf();
}
}
async updateSelf() {
const props = this.pConn$.getConfigProps() as UserReferenceProps;
this.testId = props.testId;
this.onRecordChange = props?.onRecordChange;
const { label, displayAs, value, showAsFormattedText, helperText, placeholder, displayMode } = props;
this.label$ = label;
this.showAsFormattedText$ = showAsFormattedText;
this.displayAs$ = displayAs;
this.helperText = helperText;
this.placeholder = placeholder || '';
this.displayMode$ = displayMode;
if (value && typeof value === 'object') {
this.value$ = value.userName ? value.userName : '';
} else {
this.value$ = value || '';
}
const { readOnly, required } = props;
[this.bReadonly$, this.bRequired$] = [readOnly, required].map(prop => prop === true || (typeof prop === 'string' && prop === 'true'));
this.actionsApi = this.pConn$.getActionsApi();
this.propName = this.pConn$.getStateProps().value;
this.userID$ = this.utils.getUserId(value);
if (this.userID$ && this.bReadonly$ && this.showAsFormattedText$) {
if (this.isUserNameAvailable(value)) {
this.userName$ = value.userName;
} else {
// if same user ref field is referred in view as editable & readonly formatted text
// referenced users won't be available, so get user details from dx api
// eslint-disable-next-line @typescript-eslint/no-use-before-define
this.userName$ = await getUserName(this.pConn$, this.userID$);
}
} else if (displayAs === DROPDOWN_LIST || displayAs === SEARCH_BOX) {
const queryPayload = {
dataViewName: OPERATORS_DP
};
try {
const resp = await PCore.getRestClient().invokeRestApi('getListData', { queryPayload }, ''); // 3rd arg empty string until typedef marked correctly
if (resp?.data) {
const ddDataSource = resp.data.data.map(listItem => ({
key: listItem.pyUserIdentifier,
value: listItem.pyUserName
}));
this.options$ = ddDataSource;
}
} catch (error) {
console.log(error);
}
}
}
fieldOnChange(event: any) {
if (event?.value === 'Select') {
event.value = '';
}
if (event?.target) {
this.filterValue = (event.target as HTMLInputElement).value;
}
const value = event?.value;
handleEvent(this.actionsApi, 'change', this.propName, value);
}
optionChanged(event: any) {
const value = event?.option?.value;
handleEvent(this.actionsApi, 'change', this.propName, value);
}
fieldOnBlur(event: any) {
let key = '';
if (event?.target?.value) {
const index = this.options$?.findIndex(element => element.value === event.target.value);
key = index > -1 ? (key = this.options$[index].key) : event.target.value;
}
const value = key;
handleEvent(this.actionsApi, 'changeNblur', this.propName, value);
if (this.onRecordChange) {
event.target.value = value;
this.onRecordChange(event);
}
}
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;
}
}
const buildColumnForDisplayValue = dataObj => {
if (dataObj.columns) {
dataObj.columns = dataObj.columns.map(column => {
const tempColObj = { ...column };
if (tempColObj.key === 'true') {
tempColObj.useForSearch = true;
} else {
tempColObj.useForSearch = false;
}
return tempColObj;
});
}
};
function getUserName(pConn, userId = ''): Promise<string> {
return new Promise(resolve => {
const { parameters = {}, referenceList } = pConn.getConfigProps();
const contextName = pConn.getContextName();
// eslint-disable-next-line @typescript-eslint/no-shadow
const OPERATORS_DP = referenceList || PCore.getEnvironmentInfo().getDefaultOperatorDP() || '';
const columns = [
{
value: 'pyUserName',
display: 'true',
useForSearch: true,
primary: 'true'
},
{
value: 'pyUserIdentifier',
setProperty: 'Associated property',
key: 'true',
display: 'true',
secondary: 'true',
useForSearch: true
}
];
const dataConfig: any = {
dataSource: OPERATORS_DP,
parameters,
matchPosition: 'equals',
listType: 'datapage',
columns,
cacheLifeSpan: 'form',
deferDatasource: false,
maxResultsDisplay: '1',
ignoreCase: true
};
PCore.getDataApi()
.init(dataConfig, contextName)
.then(dataApiObj => {
buildColumnForDisplayValue(dataApiObj);
dataApiObj.registerForBufferedCall({ waitTime: 50 });
dataApiObj.fetchData(userId).then((response: any) => {
resolve(response.data?.[0]?.pyUserName || userId);
});
});
});
}