Skip to content

Commit bbfd3f2

Browse files
samhere06mohas22
andauthored
Object reference, Semantic link support (#329)
* onject reference, semantic link support * updated to work with data reference case * updated autocomplete and dropdown for object reference * updated based on comments * updated to build correctly * Added new template ObjectPage --------- Co-authored-by: mohas22 <[email protected]>
1 parent 8fca075 commit bbfd3f2

26 files changed

+799
-41
lines changed

packages/angular-sdk-components/src/lib/_bridge/helpers/sdk-pega-component-map.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import { GroupComponent } from '../../_components/field/group/group.component';
3131
import { IntegerComponent } from '../../_components/field/integer/integer.component';
3232
import { ListViewActionButtonsComponent } from '../../_components/field/list-view-action-buttons/list-view-action-buttons.component';
3333
import { LocationComponent } from '../../_components/field/location/location.component';
34+
import { ObjectReferenceComponent } from '../../_components/field/object-reference/object-reference.component';
3435
import { PercentageComponent } from '../../_components/field/percentage/percentage.component';
3536
import { PhoneComponent } from '../../_components/field/phone/phone.component';
3637
import { RadioButtonsComponent } from '../../_components/field/radio-buttons/radio-buttons.component';
@@ -74,6 +75,7 @@ import { ListViewComponent } from '../../_components/template/list-view/list-vie
7475
import { MultiReferenceReadonlyComponent } from '../../_components/template/multi-reference-readonly/multi-reference-readonly.component';
7576
import { MultiselectComponent } from '../../_components/field/multiselect/multiselect.component';
7677
import { NarrowWideFormComponent } from '../../_components/template/narrow-wide-form/narrow-wide-form.component';
78+
import { ObjectPageComponent } from '../../_components/template/object-page/object-page.component';
7779
import { OneColumnComponent } from '../../_components/template/one-column/one-column.component';
7880
import { OneColumnPageComponent } from '../../_components/template/one-column-page/one-column-page.component';
7981
import { OneColumnTabComponent } from '../../_components/template/one-column-tab/one-column-tab.component';
@@ -200,6 +202,8 @@ const pegaSdkComponentMap = {
200202
NarrowWideForm: NarrowWideFormComponent,
201203
// 'NarrowWidePage': NarrowWidePage,
202204
NavBar: NavbarComponent,
205+
ObjectPage: ObjectPageComponent,
206+
ObjectReference: ObjectReferenceComponent,
203207
OneColumn: OneColumnComponent,
204208
OneColumnPage: OneColumnPageComponent,
205209
OneColumnTab: OneColumnTabComponent,

packages/angular-sdk-components/src/lib/_components/field/auto-complete/auto-complete.component.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Component, OnInit, Input, ChangeDetectorRef, forwardRef, OnDestroy } from '@angular/core';
1+
import { Component, OnInit, Input, ChangeDetectorRef, forwardRef, OnDestroy, Output, EventEmitter } from '@angular/core';
22
import { CommonModule } from '@angular/common';
33
import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
44
import { MatOptionModule } from '@angular/material/core';
@@ -47,6 +47,7 @@ interface AutoCompleteProps extends PConnFieldProps {
4747
export class AutoCompleteComponent implements OnInit, OnDestroy {
4848
@Input() pConn$: typeof PConnect;
4949
@Input() formGroup$: FormGroup;
50+
@Output() onRecordChange: EventEmitter<any> = new EventEmitter();
5051

5152
// Used with AngularPConnect
5253
angularPConnectData: AngularPConnectData = {};
@@ -331,8 +332,9 @@ export class AutoCompleteComponent implements OnInit, OnDestroy {
331332
}
332333
const value = key;
333334
handleEvent(this.actionsApi, 'changeNblur', this.propName, value);
334-
if (this.configProps$?.onRecordChange) {
335-
this.configProps$.onRecordChange(event);
335+
336+
if (this.onRecordChange) {
337+
this.onRecordChange.emit(value);
336338
}
337339
}
338340

packages/angular-sdk-components/src/lib/_components/field/dropdown/dropdown.component.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Component, OnInit, Input, ChangeDetectorRef, forwardRef, OnDestroy } from '@angular/core';
1+
import { Component, OnInit, Input, ChangeDetectorRef, forwardRef, OnDestroy, EventEmitter, Output } from '@angular/core';
22
import { CommonModule } from '@angular/common';
33
import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
44
import { MatOptionModule } from '@angular/material/core';
@@ -73,6 +73,7 @@ interface DropdownProps extends PConnFieldProps {
7373
export class DropdownComponent implements OnInit, OnDestroy {
7474
@Input() pConn$: typeof PConnect;
7575
@Input() formGroup$: FormGroup;
76+
@Output() onRecordChange: EventEmitter<any> = new EventEmitter();
7677

7778
// Used with AngularPConnect
7879
angularPConnectData: AngularPConnectData = {};
@@ -336,12 +337,13 @@ export class DropdownComponent implements OnInit, OnDestroy {
336337
event.value = '';
337338
}
338339
handleEvent(this.actionsApi, 'changeNblur', this.propName, event.value);
339-
if (this.configProps$?.onRecordChange) {
340-
this.configProps$.onRecordChange(event);
341-
}
340+
342341
this.pConn$.clearErrorMessages({
343342
property: this.propName
344343
});
344+
if (this.onRecordChange) {
345+
this.onRecordChange.emit(event.value);
346+
}
345347
}
346348

347349
getLocalizedOptionValue(opt: IOption) {
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<div *ngIf="isDisplayModeEnabled && !canBeChangedInReviewMode; else semanticLinkCheck">
2+
<component-mapper name="SingleReferenceReadOnly" [props]="{ pConn$ }"></component-mapper>
3+
</div>
4+
<ng-template #semanticLinkCheck>
5+
<div *ngIf="type === 'SemanticLink' && !canBeChangedInReviewMode; else loadDynamicComp">
6+
<component-mapper name="SemanticLink" [props]="{ pConn$: newPconn }"></component-mapper>
7+
</div>
8+
</ng-template>
9+
<ng-template #loadDynamicComp>
10+
<component-mapper
11+
*ngIf="newComponentName"
12+
[name]="newComponentName"
13+
[props]="{ pConn$: newPconn, formGroup$ }"
14+
[parent]="this"
15+
[outputEvents]="{ onRecordChange: onRecordChange }"
16+
></component-mapper>
17+
</ng-template>

packages/angular-sdk-components/src/lib/_components/field/object-reference/object-reference.component.scss

Whitespace-only changes.
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { ComponentFixture, TestBed } from '@angular/core/testing';
2+
3+
import { ObjectReferenceComponent } from './object-reference.component';
4+
5+
describe('ObjectReferenceComponent', () => {
6+
let component: ObjectReferenceComponent;
7+
let fixture: ComponentFixture<ObjectReferenceComponent>;
8+
9+
beforeEach(async () => {
10+
await TestBed.configureTestingModule({
11+
imports: [ObjectReferenceComponent]
12+
}).compileComponents();
13+
14+
fixture = TestBed.createComponent(ObjectReferenceComponent);
15+
component = fixture.componentInstance;
16+
fixture.detectChanges();
17+
});
18+
19+
it('should create', () => {
20+
expect(component).toBeTruthy();
21+
});
22+
});
Lines changed: 256 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,256 @@
1+
import { CommonModule } from '@angular/common';
2+
import { Component, Input, OnInit, forwardRef, OnDestroy } from '@angular/core';
3+
import { FormGroup } from '@angular/forms';
4+
import { ComponentMetadataConfig } from '@pega/pcore-pconnect-typedefs/interpreter/types';
5+
import { AngularPConnectData, AngularPConnectService } from '../../../_bridge/angular-pconnect';
6+
import { ComponentMapperComponent } from '../../../_bridge/component-mapper/component-mapper.component';
7+
import { generateColumns, getDataRelationshipContextFromKey } from '../../../_helpers/objectReference-utils';
8+
import { PConnFieldProps } from '../../../_types/PConnProps.interface';
9+
10+
interface ObjectReferenceProps extends PConnFieldProps {
11+
showPromotedFilters: boolean;
12+
inline: boolean;
13+
parameters: Object;
14+
mode: string;
15+
targetObjectType: any;
16+
allowAndPersistChangesInReviewMode: boolean;
17+
}
18+
19+
@Component({
20+
selector: 'app-object-reference',
21+
imports: [CommonModule, forwardRef(() => ComponentMapperComponent)],
22+
templateUrl: './object-reference.component.html',
23+
styleUrl: './object-reference.component.scss'
24+
})
25+
export class ObjectReferenceComponent implements OnInit, OnDestroy {
26+
@Input() pConn$: typeof PConnect;
27+
@Input() formGroup$: FormGroup;
28+
29+
angularPConnectData: AngularPConnectData = {};
30+
configProps: ObjectReferenceProps;
31+
value: { [key: string]: any };
32+
readOnly: boolean;
33+
isForm: boolean;
34+
type: string;
35+
isDisplayModeEnabled: boolean;
36+
canBeChangedInReviewMode: boolean;
37+
newComponentName: string;
38+
newPconn: typeof PConnect;
39+
rawViewMetadata: ComponentMetadataConfig | undefined;
40+
41+
constructor(private angularPConnect: AngularPConnectService) {}
42+
43+
ngOnInit() {
44+
this.angularPConnectData = this.angularPConnect.registerAndSubscribeComponent(this, this.onStateChange);
45+
this.checkAndUpdate();
46+
}
47+
48+
onStateChange() {
49+
this.checkAndUpdate();
50+
}
51+
52+
ngOnDestroy() {
53+
if (this.angularPConnectData.unsubscribeFn) {
54+
this.angularPConnectData.unsubscribeFn();
55+
}
56+
}
57+
58+
checkAndUpdate() {
59+
const shouldUpdate = this.angularPConnect.shouldComponentUpdate(this);
60+
if (shouldUpdate) {
61+
this.updateSelf();
62+
}
63+
}
64+
65+
updateSelf() {
66+
this.configProps = this.pConn$.resolveConfigProps(this.pConn$.getConfigProps()) as ObjectReferenceProps;
67+
const displayMode = this.configProps.displayMode;
68+
const editableInReview = this.configProps.allowAndPersistChangesInReviewMode ?? false;
69+
const targetObjectType = this.configProps.targetObjectType;
70+
const mode = this.configProps.mode;
71+
const parameters = this.configProps.parameters;
72+
const hideLabel = this.configProps.hideLabel;
73+
const inline = this.configProps.inline;
74+
const showPromotedFilters = this.configProps.showPromotedFilters;
75+
const referenceType: string = targetObjectType === 'case' ? 'Case' : 'Data';
76+
this.rawViewMetadata = this.pConn$.getRawMetadata();
77+
const refFieldMetadata = this.pConn$.getFieldMetadata(this.rawViewMetadata?.config?.value?.split('.', 2)[1] ?? '');
78+
79+
// Destructured properties
80+
const propsToUse = { ...this.pConn$.getInheritedProps(), ...this.configProps };
81+
82+
// Computed variables
83+
this.isDisplayModeEnabled = displayMode === 'DISPLAY_ONLY';
84+
this.canBeChangedInReviewMode = editableInReview && ['Autocomplete', 'Dropdown'].includes((this.rawViewMetadata?.config as any)?.componentType);
85+
// componentType is not defined in ComponentMetadataConfig type so using any
86+
this.type = (this.rawViewMetadata?.config as any)?.componentType;
87+
88+
if (this.type === 'SemanticLink' && !this.canBeChangedInReviewMode) {
89+
const config: any = {
90+
...this.rawViewMetadata?.config,
91+
primaryField: (this.rawViewMetadata?.config as any).displayField
92+
};
93+
config.caseClass = (this.rawViewMetadata?.config as any).targetObjectClass;
94+
config.text = config.primaryField;
95+
config.caseID = config.value;
96+
config.contextPage = `@P .${
97+
(this.rawViewMetadata?.config as any)?.displayField
98+
? getDataRelationshipContextFromKey((this.rawViewMetadata?.config as any).displayField)
99+
: null
100+
}`;
101+
config.resourceParams = {
102+
workID: config.value
103+
};
104+
config.resourcePayload = {
105+
caseClassName: config.caseClass
106+
};
107+
108+
const component = this.pConn$.createComponent(
109+
{
110+
type: 'SemanticLink',
111+
config: {
112+
...config,
113+
displayMode,
114+
referenceType,
115+
hideLabel,
116+
dataRelationshipContext: (this.rawViewMetadata?.config as any)?.displayField
117+
? getDataRelationshipContextFromKey((this.rawViewMetadata?.config as any).displayField)
118+
: null
119+
}
120+
},
121+
'',
122+
0,
123+
{}
124+
);
125+
this.newPconn = component?.getPConnect();
126+
}
127+
128+
if (this.type !== 'SemanticLink' && !this.isDisplayModeEnabled) {
129+
// 1) Set datasource
130+
const config: any = { ...this.rawViewMetadata?.config };
131+
generateColumns(config, this.pConn$, referenceType);
132+
config.deferDatasource = true;
133+
config.listType = 'datapage';
134+
if (['Dropdown', 'AutoComplete'].includes(this.type) && !config.placeholder) {
135+
config.placeholder = '@L Select...';
136+
}
137+
138+
// 2) Pass through configs
139+
config.showPromotedFilters = showPromotedFilters;
140+
141+
if (!this.canBeChangedInReviewMode) {
142+
config.displayMode = displayMode;
143+
}
144+
145+
// 3) Define field meta
146+
147+
const fieldMetaData = {
148+
datasourceMetadata: {
149+
datasource: {
150+
parameters: {},
151+
propertyForDisplayText: false,
152+
propertyForValue: false,
153+
name: ''
154+
}
155+
}
156+
};
157+
if (config?.parameters) {
158+
fieldMetaData.datasourceMetadata.datasource.parameters = parameters;
159+
}
160+
fieldMetaData.datasourceMetadata.datasource.propertyForDisplayText = config?.datasource?.fields?.text?.startsWith('@P')
161+
? config?.datasource?.fields?.text?.substring(3)
162+
: config?.datasource?.fields?.text;
163+
fieldMetaData.datasourceMetadata.datasource.propertyForValue = config?.datasource?.fields?.value?.startsWith('@P')
164+
? config?.datasource?.fields?.value?.substring(3)
165+
: config?.datasource?.fields?.value;
166+
fieldMetaData.datasourceMetadata.datasource.name = config?.referenceList ?? '';
167+
168+
const component = this.pConn$.createComponent(
169+
{
170+
type: this.type,
171+
config: {
172+
...config,
173+
descriptors: mode === 'single' ? refFieldMetadata?.descriptors : null,
174+
datasourceMetadata: fieldMetaData?.datasourceMetadata,
175+
required: propsToUse.required,
176+
visibility: propsToUse.visibility,
177+
disabled: propsToUse.disabled,
178+
label: propsToUse.label,
179+
parameters: config.parameters,
180+
readOnly: false,
181+
localeReference: config.localeReference,
182+
...(mode === 'single' ? { referenceType } : ''),
183+
contextClass: config.targetObjectClass,
184+
primaryField: config?.displayField,
185+
dataRelationshipContext: config?.displayField ? getDataRelationshipContextFromKey(config.displayField) : null,
186+
hideLabel,
187+
inline
188+
}
189+
},
190+
'',
191+
0,
192+
{}
193+
);
194+
this.newComponentName = component?.getPConnect().getComponentName();
195+
this.newPconn = component?.getPConnect();
196+
if (this.rawViewMetadata?.config) {
197+
this.rawViewMetadata.config = config ? { ...config } : this.rawViewMetadata.config;
198+
}
199+
}
200+
}
201+
202+
onRecordChange(value) {
203+
const caseKey = this.pConn$.getCaseInfo().getKey() ?? '';
204+
const refreshOptions = { autoDetectRefresh: true, propertyName: '' };
205+
refreshOptions.propertyName = this.rawViewMetadata?.config?.value ?? '';
206+
207+
if (!this.canBeChangedInReviewMode || !this.pConn$.getValue('__currentPageTabViewName')) {
208+
const pgRef = this.pConn$.getPageReference().replace('caseInfo.content', '') ?? '';
209+
const viewName = this.rawViewMetadata?.name;
210+
if (viewName && viewName.length > 0) {
211+
getPConnect().getActionsApi().refreshCaseView(caseKey, viewName, pgRef, refreshOptions);
212+
}
213+
}
214+
215+
const propValue = value;
216+
const propName =
217+
this.rawViewMetadata?.type === 'SimpleTableSelect' && this.configProps.mode === 'multi'
218+
? PCore.getAnnotationUtils().getPropertyName(this.rawViewMetadata?.config?.selectionList ?? '')
219+
: PCore.getAnnotationUtils().getPropertyName(this.rawViewMetadata?.config?.value ?? '');
220+
221+
if (propValue && this.canBeChangedInReviewMode && this.isDisplayModeEnabled) {
222+
PCore.getCaseUtils()
223+
.getCaseEditLock(caseKey, '')
224+
.then(caseResponse => {
225+
const pageTokens = this.pConn$.getPageReference().replace('caseInfo.content', '').split('.');
226+
let curr = {};
227+
const commitData = curr;
228+
229+
pageTokens?.forEach(el => {
230+
if (el !== '') {
231+
curr[el] = {};
232+
curr = curr[el];
233+
}
234+
});
235+
236+
// expecting format like {Customer: {pyID:"C-100"}}
237+
const propArr = propName.split('.');
238+
propArr.forEach((element, idx) => {
239+
if (idx + 1 === propArr.length) {
240+
curr[element] = propValue;
241+
} else {
242+
curr[element] = {};
243+
curr = curr[element];
244+
}
245+
});
246+
247+
PCore.getCaseUtils()
248+
.updateCaseEditFieldsData(caseKey, { [caseKey]: commitData }, caseResponse.headers.etag, this.pConn$?.getContextName() ?? '')
249+
.then(response => {
250+
PCore.getContainerUtils().updateParentLastUpdateTime(this.pConn$.getContextName() ?? '', response.data.data.caseInfo.lastUpdateTime);
251+
PCore.getContainerUtils().updateRelatedContextEtag(this.pConn$.getContextName() ?? '', response.headers.etag);
252+
});
253+
});
254+
}
255+
}
256+
}

packages/angular-sdk-components/src/lib/_components/field/selectable-card/selectable-card.component.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ export class SelectableCardComponent implements OnInit, OnDestroy {
179179

180180
this.selectionList = this.configProps$.selectionList;
181181
this.selectedvalues = this.configProps$.readonlyContextList;
182-
this.showNoValue = this.readOnly && this.selectedvalues.length === 0; // not used
182+
this.showNoValue = this.readOnly && this.selectedvalues?.length === 0; // not used
183183
this.primaryField = this.configProps$.primaryField;
184184
}
185185

0 commit comments

Comments
 (0)