Skip to content

Commit 5a4a6e4

Browse files
authored
fix: show field level validation messages in Error banner (#288)
* fix: show field level validation messages in Error banner
1 parent e046232 commit 5a4a6e4

File tree

5 files changed

+90
-42
lines changed

5 files changed

+90
-42
lines changed

packages/angular-sdk-components/src/lib/_components/infra/Containers/flow-container/flow-container.component.ts

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,14 @@ export class FlowContainerComponent extends FlowContainerBaseComponent implement
121121
},
122122
'cancelPressed'
123123
);
124+
125+
PCore.getPubSubUtils().subscribe(
126+
'clearBannerMessages',
127+
() => {
128+
this.banners = [];
129+
},
130+
'clearBannerMessages'
131+
);
124132
}
125133

126134
ngOnDestroy() {
@@ -131,6 +139,8 @@ export class FlowContainerComponent extends FlowContainerBaseComponent implement
131139
PCore.getPubSubUtils().unsubscribe(PCore.getConstants().PUB_SUB_EVENTS.EVENT_CANCEL, 'cancelAssignment');
132140

133141
PCore.getPubSubUtils().unsubscribe('cancelPressed', 'cancelPressed');
142+
143+
PCore.getPubSubUtils().unsubscribe('clearBannerMessages', 'clearBannerMessages');
134144
}
135145

136146
handleCancel() {
@@ -155,13 +165,15 @@ export class FlowContainerComponent extends FlowContainerBaseComponent implement
155165
const caseViewModeFromProps = this.angularPConnect.getComponentProp(this, 'caseViewMode');
156166
const caseViewModeFromRedux = pConn.getValue('context_data.caseViewMode', '');
157167

168+
const completeProps = this.angularPConnect.getCurrentCompleteProps(this) as FlowContainerProps;
169+
158170
// ONLY call updateSelf when the component should update
159171
// AND removing the "gate" that was put there since shouldComponentUpdate
160172
// should be the real "gate"
173+
// eslint-disable-next-line sonarjs/no-collapsible-if
161174
if (bUpdateSelf || caseViewModeFromProps !== caseViewModeFromRedux) {
162175
// don't want to redraw the flow container when there are page messages, because
163176
// the redraw causes us to loose the errors on the elements
164-
const completeProps = this.angularPConnect.getCurrentCompleteProps(this) as FlowContainerProps;
165177
if (!completeProps.pageMessages || completeProps.pageMessages.length == 0) {
166178
// with a cancel, need to timeout so todo will update correctly
167179
if (this.bHasCancel) {
@@ -172,10 +184,10 @@ export class FlowContainerComponent extends FlowContainerBaseComponent implement
172184
} else {
173185
this.updateSelf();
174186
}
175-
} else {
176-
this.showPageMessages(completeProps);
177187
}
178188
}
189+
190+
this.showPageMessages(completeProps);
179191
}
180192

181193
showPageMessages(completeProps: FlowContainerProps) {

packages/angular-sdk-components/src/lib/_components/infra/assignment/assignment.component.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<div>
2-
<div><component-mapper name="AlertBanner" [props]="{ banners }" [parent]="this"></component-mapper></div>
2+
<div><component-mapper name="AlertBanner" [props]="{ banners: bannerService.banners }" [parent]="this"></component-mapper></div>
33
<div *ngIf="bHasNavigation$" class="psdk-stepper">
44
<component-mapper
55
name="MultiStep"

packages/angular-sdk-components/src/lib/_components/infra/assignment/assignment.component.ts

Lines changed: 41 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { FormGroup } from '@angular/forms';
66
import { AngularPConnectData, AngularPConnectService } from '../../../_bridge/angular-pconnect';
77
import { ErrorMessagesService } from '../../../_messages/error-messages.service';
88
import { ProgressSpinnerService } from '../../../_messages/progress-spinner.service';
9+
import { BannerService } from '../../../_services/banner.service';
910
import { ReferenceComponent } from '../../infra/reference/reference.component';
1011
import { ComponentMapperComponent } from '../../../_bridge/component-mapper/component-mapper.component';
1112

@@ -37,7 +38,6 @@ export class AssignmentComponent implements OnInit, OnDestroy, OnChanges {
3738
@Input() isCreateStage$: boolean;
3839
@Input() updateToken$: number;
3940
@Input() isInModal$ = false;
40-
@Input() banners;
4141

4242
// For interaction with AngularPConnect
4343
angularPConnectData: AngularPConnectData = {};
@@ -77,12 +77,15 @@ export class AssignmentComponent implements OnInit, OnDestroy, OnChanges {
7777
localeCategory = 'Assignment';
7878
localeReference;
7979

80+
snackBarRef;
81+
8082
constructor(
8183
private angularPConnect: AngularPConnectService,
8284
private psService: ProgressSpinnerService,
8385
private erService: ErrorMessagesService,
8486
private ngZone: NgZone,
85-
private snackBar: MatSnackBar
87+
private snackBar: MatSnackBar,
88+
public bannerService: BannerService
8689
) {}
8790

8891
ngOnInit(): void {
@@ -113,6 +116,8 @@ export class AssignmentComponent implements OnInit, OnDestroy, OnChanges {
113116
// Should always check the bridge to see if the component should update itself (re-render)
114117
const bUpdateSelf = this.angularPConnect.shouldComponentUpdate(this);
115118

119+
this.bannerService.updateBanners(this.itemKey$);
120+
116121
// ONLY call updateSelf when the component should update
117122
// AND removing the "gate" that was put there since shouldComponentUpdate
118123
// should be the real "gate"
@@ -295,6 +300,9 @@ export class AssignmentComponent implements OnInit, OnDestroy, OnChanges {
295300
}
296301

297302
buttonClick(sAction, sButtonType) {
303+
this.snackBarRef?.dismiss();
304+
this.bannerService.clearBanners();
305+
PCore.getPubSubUtils().publish('clearBannerMessages');
298306
// right now, done on an individual basis, setting bReInit to true
299307
// upon the next flow container state change, will cause the flow container
300308
// to re-initialize
@@ -318,21 +326,21 @@ export class AssignmentComponent implements OnInit, OnDestroy, OnChanges {
318326
switch (sAction) {
319327
case 'navigateToStep':
320328
this.erService.sendMessage('publish', '');
321-
if (this.formValid()) {
322-
this.bReInit = true;
323-
this.psService.sendMessage(true);
324329

325-
const navigatePromise = this.navigateToStep('previous', this.itemKey$);
326-
navigatePromise
327-
.then(() => {
328-
this.updateChanges();
329-
this.psService.sendMessage(false);
330-
})
331-
.catch(() => {
332-
this.psService.sendMessage(false);
333-
this.snackBar.open(`${this.localizedVal('Navigation failed!', this.localeCategory)}`, 'Ok');
334-
});
335-
}
330+
this.bReInit = true;
331+
this.psService.sendMessage(true);
332+
333+
const navigatePromise = this.navigateToStep('previous', this.itemKey$);
334+
navigatePromise
335+
.then(() => {
336+
this.updateChanges();
337+
this.psService.sendMessage(false);
338+
})
339+
.catch(() => {
340+
this.psService.sendMessage(false);
341+
this.snackBarRef = this.snackBar.open(`${this.localizedVal('Navigation failed!', this.localeCategory)}`, 'Ok');
342+
});
343+
336344
break;
337345

338346
case 'saveAssignment': {
@@ -348,7 +356,7 @@ export class AssignmentComponent implements OnInit, OnDestroy, OnChanges {
348356
})
349357
.catch(() => {
350358
this.psService.sendMessage(false);
351-
this.snackBar.open(`${this.localizedVal('Save failed', this.localeCategory)}`, 'Ok');
359+
this.snackBarRef = this.snackBar.open(`${this.localizedVal('Save failed', this.localeCategory)}`, 'Ok');
352360
});
353361

354362
break;
@@ -372,7 +380,7 @@ export class AssignmentComponent implements OnInit, OnDestroy, OnChanges {
372380
})
373381
.catch(() => {
374382
this.psService.sendMessage(false);
375-
this.snackBar.open(`${this.localizedVal('Cancel failed!', this.localeCategory)}`, 'Ok');
383+
this.snackBarRef = this.snackBar.open(`${this.localizedVal('Cancel failed!', this.localeCategory)}`, 'Ok');
376384
});
377385
} else {
378386
this.psService.sendMessage(true);
@@ -389,7 +397,7 @@ export class AssignmentComponent implements OnInit, OnDestroy, OnChanges {
389397
})
390398
.catch(() => {
391399
this.psService.sendMessage(false);
392-
this.snackBar.open(`${this.localizedVal('Cancel failed!', this.localeCategory)}`, 'Ok');
400+
this.snackBarRef = this.snackBar.open(`${this.localizedVal('Cancel failed!', this.localeCategory)}`, 'Ok');
393401
});
394402
}
395403
break;
@@ -401,7 +409,7 @@ export class AssignmentComponent implements OnInit, OnDestroy, OnChanges {
401409
.then(() => {})
402410
.catch(() => {
403411
this.psService.sendMessage(false);
404-
this.snackBar.open(`${this.localizedVal('Rejection failed!', this.localeCategory)}`, 'Ok');
412+
this.snackBarRef = this.snackBar.open(`${this.localizedVal('Rejection failed!', this.localeCategory)}`, 'Ok');
405413
});
406414

407415
break;
@@ -414,23 +422,18 @@ export class AssignmentComponent implements OnInit, OnDestroy, OnChanges {
414422
switch (sAction) {
415423
case 'finishAssignment':
416424
this.erService.sendMessage('publish', '');
417-
if (this.formValid()) {
418-
this.bReInit = true;
419-
this.psService.sendMessage(true);
420-
const finishPromise = this.finishAssignment(this.itemKey$); // JA - was itemID but Nebula/Constellation uses itemKey
421-
finishPromise
422-
.then(() => {
423-
this.psService.sendMessage(false);
424-
this.updateChanges();
425-
})
426-
.catch(() => {
427-
this.psService.sendMessage(false);
428-
this.snackBar.open(`${this.localizedVal('Submit failed!', this.localeCategory)}`, 'Ok');
429-
});
430-
} else {
431-
// let snackBarRef = this.snackBar.open("Please fix errors on form.", "Ok");
432-
this.erService.sendMessage('show', this.localizedVal('Please fix errors on form.', this.localeCategory));
433-
}
425+
this.bReInit = true;
426+
this.psService.sendMessage(true);
427+
const finishPromise = this.finishAssignment(this.itemKey$); // JA - was itemID but Nebula/Constellation uses itemKey
428+
finishPromise
429+
.then(() => {
430+
this.psService.sendMessage(false);
431+
this.updateChanges();
432+
})
433+
.catch(() => {
434+
this.psService.sendMessage(false);
435+
this.snackBarRef = this.snackBar.open(`${this.localizedVal('Submit failed!', this.localeCategory)}`, 'Ok');
436+
});
434437
break;
435438

436439
case 'approveCase': {
@@ -440,7 +443,7 @@ export class AssignmentComponent implements OnInit, OnDestroy, OnChanges {
440443
.then(() => {})
441444
.catch(() => {
442445
this.psService.sendMessage(false);
443-
this.snackBar.open(`${this.localizedVal('Approve failed!', this.localeCategory)}`, 'Ok');
446+
this.snackBarRef = this.snackBar.open(`${this.localizedVal('Approve failed!', this.localeCategory)}`, 'Ok');
444447
});
445448

446449
break;
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { Injectable } from '@angular/core';
2+
3+
@Injectable({
4+
providedIn: 'root'
5+
})
6+
export class BannerService {
7+
banners: any[] = [];
8+
9+
clearBanners() {
10+
this.banners = [];
11+
}
12+
13+
updateBanners(itemKey) {
14+
const localizedValue = PCore.getLocaleUtils().getLocaleValue;
15+
const validationErrors = PCore.getMessageManager().getValidationErrorMessages(itemKey) || [];
16+
17+
const formattedErrors = validationErrors.map(error => {
18+
let message = '';
19+
20+
if (typeof error === 'string') {
21+
message = error;
22+
} else {
23+
error.label = error.label.endsWith(':') ? error.label : `${error.label}:`;
24+
message = `${error.label} ${error.description}`;
25+
}
26+
27+
return localizedValue(message, 'Messages');
28+
});
29+
30+
this.banners = formattedErrors.length ? [{ messages: formattedErrors, variant: 'urgent' }] : [];
31+
}
32+
}

packages/angular-sdk-components/src/public-api.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ export * from './lib/_directives/thousand-seperator.directive';
126126
export * from './lib/_services/server-config.service';
127127
export * from './lib/_services/case.service';
128128
export * from './lib/_services/datapage.service';
129+
export * from './lib/_services/banner.service';
129130
export * from './lib/_services/endpoints';
130131

131132
export * from './lib/_helpers/case-utils';

0 commit comments

Comments
 (0)