import { Component, OnInit, Input, forwardRef, SimpleChanges, OnDestroy, OnChanges } from '@angular/core'; import { CommonModule } from '@angular/common'; import { FormGroup } from '@angular/forms'; import { AngularPConnectData, AngularPConnectService } from '@pega/angular-sdk-components'; import { Utils } from '@pega/angular-sdk-components'; import { getAllFields } from '@pega/angular-sdk-components'; import { ComponentMapperComponent } from '@pega/angular-sdk-components'; const NO_HEADER_TEMPLATES = ['SubTabs', 'SimpleTable', 'Confirmation', 'DynamicTabs', 'DetailsSubTabs', 'ListView']; const DETAILS_TEMPLATES = [ 'Details', 'DetailsFields', 'DetailsOneColumn', 'DetailsSubTabs', 'DetailsThreeColumn', 'DetailsTwoColumn', 'NarrowWideDetails', 'WideNarrowDetails' ]; function isDetailsTemplate(template) { return DETAILS_TEMPLATES.includes(template); } /** * WARNING: This file is part of the infrastructure component responsible for working with Redux and managing the creation and update of Redux containers and PConnect. * You may override Material components within this component if needed, but do not modify any container-related logic. Changing this logic can lead to unexpected behavior. */ /** * * @param pConnn - PConnect Object * @returns visibility expression result if exists, otherwise true */ function evaluateVisibility(pConnn) { let bVisibility = true; const sVisibility = pConnn.meta.config.visibility; if (sVisibility && sVisibility.length) { // e.g. "@E .EmbeddedData_SelectedTestName == 'Readonly' && .EmbeddedData_SelectedSubCategory == 'Mode'" const aVisibility = sVisibility.split('&&'); // e.g. ["EmbeddedData_SelectedTestName": "Readonly", "EmbeddedData_SelectedSubCategory": "Mode"] const context = pConnn.getContextName(); // Reading values from the Store to evaluate the visibility expressions const storeData = PCore.getStore().getState()?.data[context].caseInfo.content; const initialVal = {}; const oProperties = aVisibility.reduce((properties, property) => { const keyStartIndex = property.indexOf('.'); const keyEndIndex = property.indexOf('=') - 1; const valueStartIndex = property.indexOf("'"); const valueEndIndex = property.lastIndexOf("'") - 1; return { ...properties, [property.substr(keyStartIndex + 1, keyEndIndex - keyStartIndex - 1)]: property.substr(valueStartIndex + 1, valueEndIndex - valueStartIndex) }; }, initialVal); const propertyKeys = Object.keys(oProperties); const propertyValues = Object.values(oProperties); for (let propertyIndex = 0; propertyIndex < propertyKeys.length; propertyIndex++) { if (storeData[propertyKeys[propertyIndex]] !== propertyValues[propertyIndex]) { bVisibility = false; } } } return bVisibility; } interface ViewProps { // If any, enter additional props that only exist on this component template?: string; label?: string; showLabel: boolean; title?: string; visibility?: boolean; } @Component({ selector: 'app-view', templateUrl: './view.component.html', styleUrls: ['./view.component.scss'], imports: [CommonModule, forwardRef(() => ComponentMapperComponent)] }) export class ViewComponent implements OnInit, OnDestroy, OnChanges { @Input() pConn$: typeof PConnect; @Input() formGroup$: FormGroup; @Input() displayOnlyFA$: boolean; // @Input() updateToken$: number; angularPConnectData: AngularPConnectData = {}; noHeaderTemplates = NO_HEADER_TEMPLATES; configProps$: ViewProps; inheritedProps$: any; arChildren$: any[]; templateName$: string; title$ = ''; label$ = ''; showLabel$ = false; visibility$ = true; constructor( private angularPConnect: AngularPConnectService, private utils: Utils ) {} ngOnInit() { // First thing in initialization is registering and subscribing to the AngularPConnect service this.angularPConnectData = this.angularPConnect.registerAndSubscribeComponent(this, this.onStateChange); this.checkAndUpdate(); } // 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(); } } ngOnChanges(changes: SimpleChanges) { const { pConn$ } = changes; if (pConn$.previousValue && pConn$.previousValue !== pConn$.currentValue) { this.checkAndUpdate(); } } updateSelf() { if (this.angularPConnect.getComponentID(this) === undefined) { return; } // debugger; this.configProps$ = this.pConn$.resolveConfigProps(this.pConn$.getConfigProps()) as ViewProps; this.inheritedProps$ = this.pConn$.getInheritedProps(); // NOTE: this.configProps$.visibility'] is used in view.component.ts such that // the View will only be rendered when this.configProps$.visibility'] is false. // It WILL render if true or undefined. this.templateName$ = this.configProps$.template || ''; this.title$ = this.configProps$.title || ''; // label & showLabel within inheritedProps takes precedence over configProps this.label$ = this.inheritedProps$.label || this.configProps$.label || ''; this.showLabel$ = this.inheritedProps$.showLabel || this.configProps$.showLabel || isDetailsTemplate(this.templateName$); this.arChildren$ = this.pConn$.getChildren(); this.visibility$ = this.configProps$.visibility ?? this.visibility$; /** * In instances where there is context, like with "shippingAddress," the pageReference becomes "caseInfo.content.shippingAddress." * This leads to problems in the getProperty API, as it incorrectly assesses the visibility condition by looking in the wrong location * in the Store for the property values. Reference component should be able to handle such scenarios(as done in SDK-R) since it has the * expected pageReference values, the View component currently cannot handle this. * The resolution lies in transferring this responsibility to the Reference component, eliminating the need for this code when Reference * component is able to handle it. */ if (!this.configProps$.visibility && this.pConn$.getPageReference().length > 'caseInfo.content'.length) { this.visibility$ = evaluateVisibility(this.pConn$); } // was: this.arChildren$ = this.pConn$.getChildren() as Array; // debug // let kidList: string = ""; // for (let i in this.arChildren$) { // kidList = kidList.concat(this.arChildren$[i].getPConnect().getComponentName()).concat(","); // } // console.log("-->view update: " + this.angularPConnect.getComponentID(this) + ", template: " + this.templateName$ + ", kids: " + kidList); } // JA - adapting additionalProps from Nebula/Constellation version which uses static methods // on the component classes stored in PComponents (that Angular doesn't have)... additionalProps(state: any, getPConnect: any) { let propObj = {}; // We already have the template name in this.templateName$ if (this.templateName$ !== '') { let allFields = {}; // These uses are adapted from Nebula/Constellation CaseSummary.additionalProps switch (this.templateName$) { case 'CaseSummary': allFields = getAllFields(getPConnect); // eslint-disable-next-line no-case-declarations const unresFields = { primaryFields: allFields[0], secondaryFields: allFields[1] }; propObj = getPConnect.resolveConfigProps(unresFields); break; case 'Details': allFields = getAllFields(getPConnect); propObj = { fields: allFields[0] }; break; default: break; } } return propObj; } ngOnDestroy(): void { if (this.angularPConnectData.unsubscribeFn) { this.angularPConnectData.unsubscribeFn(); } } }