Dataset Wizard. Table Of Contents entries show error state (not visible yet, only implementation). Fix Bug on DMP Editor: "Save & Add" doesn't validate datasets to proceed anymore.

This commit is contained in:
Kristian Ntavidi 2021-03-19 12:04:01 +02:00
parent 6f52f77459
commit 97db10be12
9 changed files with 176 additions and 19 deletions

View File

@ -54,7 +54,7 @@
<div (click)="changeStep(0)" *ngIf="datasetInfoValid()" class="main-info" [ngClass]="{'active': this.step === 0}">0. {{'DMP-EDITOR.STEPPER.MAIN-INFO' | translate}} (<mat-icon class="done-icon">done</mat-icon>)</div>
<div class="row toc-pane-container" #boundary (click)="changeStep(1)">
<div #spacer></div>
<table-of-contents [TOCENTRY_ID_PREFIX]="TOCENTRY_ID_PREFIX" [hasFocus]="step === 1" [formGroup]="formGroup" *ngIf="formGroup && formGroup.get('datasetProfileDefinition')" [links]="links" [boundary]="boundary" [spacer]="spacer" [isActive]="step !== 0" stickyThing (stepFound)="onStepFound($event)" (currentLinks)="getLinks($event)" [visibilityRules]="formGroup.get('datasetProfileDefinition').get('rules').value"></table-of-contents>
<table-of-contents [showErrors]="showtocentriesErrors" [TOCENTRY_ID_PREFIX]="TOCENTRY_ID_PREFIX" [hasFocus]="step === 1" [formGroup]="formGroup" *ngIf="formGroup && formGroup.get('datasetProfileDefinition')" [links]="links" [boundary]="boundary" [spacer]="spacer" [isActive]="step !== 0" stickyThing (stepFound)="onStepFound($event)" (currentLinks)="getLinks($event)" [visibilityRules]="formGroup.get('datasetProfileDefinition').get('rules').value"></table-of-contents>
</div>
</div>
</div>
@ -88,6 +88,9 @@
</div>
</div>
<!-- <mat-slide-toggle [(ngModel)]="showtocentriesErrors">
</mat-slide-toggle> -->
<!-- <div class="row">
<div class="col-12">

View File

@ -92,6 +92,7 @@ export class DatasetWizardComponent extends BaseComponent implements OnInit, IBr
links: Link[] = [];
//the table seraches for elements to scroll on page with id (TOCENTRY_ID_PREFIX+fieldsetId<Tocentry>)
TOCENTRY_ID_PREFIX="TocEntRy";
showtocentriesErrors = false;
constructor(
private datasetWizardService: DatasetWizardService,
@ -615,7 +616,7 @@ export class DatasetWizardComponent extends BaseComponent implements OnInit, IBr
if(aControl.invalid){
if(aControl.errors){//although here should always be true
if(aControl.errors){
//check if has placeholder
if( (<any>aControl).nativeElement !== undefined && (<any>aControl).nativeElement !== null){
controlName = this._getPlaceHolder(aControl);
@ -625,6 +626,9 @@ export class DatasetWizardComponent extends BaseComponent implements OnInit, IBr
errmess.push(...errorMessage);
}
/*in case the aControl is FormControl then the it should have provided its error messages above.
No need to check case of FormControl below*/
if(aControl instanceof FormGroup){
const fg = aControl as FormGroup;

View File

@ -429,7 +429,20 @@ export class DmpEditorComponent extends BaseComponent implements OnInit, IBreadC
return this.formGroup.get('datasets')['controls'][0].valid;
}
private showValidationErrorsDialog(projectOnly?: boolean) {
private showValidationErrorsDialog(projectOnly?: boolean, errmess?: string[]) {
if(errmess){
const dialogRef = this.dialog.open(FormValidationErrorsDialogComponent, {
disableClose: true,
autoFocus: false,
restoreFocus: false,
data: {
errorMessages:errmess,
projectOnly: projectOnly
},
});
}else{
const dialogRef = this.dialog.open(FormValidationErrorsDialogComponent, {
disableClose: true,
autoFocus: false,
@ -441,6 +454,8 @@ export class DmpEditorComponent extends BaseComponent implements OnInit, IBreadC
});
}
}
onSubmit(addNew?: boolean, showAddDatasetDialog?: boolean): void {
this.scrollTop = document.getElementById('editor-form').scrollTop;
@ -734,8 +749,124 @@ export class DmpEditorComponent extends BaseComponent implements OnInit, IBreadC
this.formSubmit(true);
}
//checks if the dpm is valid not taking into account the datasets validity
private _isDMPDescriptionValid():boolean{
const form: FormGroup = this.formGroup;
if(form.controls){
return Object.keys(form.controls)
.map(controlName=>{//get validity of each control
if(controlName === 'datasets'){//we dont care if datasets are valid
return true;
}
return !form.get(controlName).invalid;//!! in case the control is disabled, we consider it valid
})
.reduce((isFormValid,isControlValid)=>{//aggregate validities
return isControlValid && isFormValid;
}, true);
}
return true;
}
private _getErrorMessage(formControl: AbstractControl, name: string): string[] {
const errors: string[] = [];
Object.keys(formControl.errors).forEach(key => {
if (key === 'required') { errors.push(this.language.instant(name + ": " + this.language.instant('GENERAL.FORM-VALIDATION-DISPLAY-DIALOG.REQUIRED'))); }
// if (key === 'required') { errors.push(this.language.instant('GENERAL.FORM-VALIDATION-DISPLAY-DIALOG.THIS-FIELD') + ' "' + this.getPlaceHolder(formControl) + '" ' + this.language.instant('GENERAL.FORM-VALIDATION-DISPLAY-DIALOG.HAS-ERROR') + ', ' + this.language.instant('GENERAL.FORM-VALIDATION-DISPLAY-DIALOG.REQUIRED')); }
else if (key === 'email') { errors.push(this.language.instant('GENERAL.FORM-VALIDATION-DISPLAY-DIALOG.THIS-FIELD') + ' "' + name + '" ' + this.language.instant('GENERAL.FORM-VALIDATION-DISPLAY-DIALOG.HAS-ERROR') + ', ' + this.language.instant('GENERAL.FORM-VALIDATION-DISPLAY-DIALOG.EMAIL')); }
else if (key === 'min') { errors.push(this.language.instant('GENERAL.FORM-VALIDATION-DISPLAY-DIALOG.THIS-FIELD') + ' "' + name + '" ' + this.language.instant('GENERAL.FORM-VALIDATION-DISPLAY-DIALOG.HAS-ERROR') + ', ' + this.language.instant('GENERAL.FORM-VALIDATION-DISPLAY-DIALOG.MIN-VALUE', { 'min': formControl.getError('min').min })); }
else if (key === 'max') { errors.push(this.language.instant('GENERAL.FORM-VALIDATION-DISPLAY-DIALOG.THIS-FIELD') + ' "' + name + '" ' + this.language.instant('GENERAL.FORM-VALIDATION-DISPLAY-DIALOG.HAS-ERROR') + ', ' + this.language.instant('GENERAL.FORM-VALIDATION-DISPLAY-DIALOG.MAX-VALUE', { 'max': formControl.getError('max').max })); }
else { errors.push(this.language.instant('GENERAL.FORM-VALIDATION-DISPLAY-DIALOG.THIS-FIELD') + ' "' + name + '" ' + this.language.instant('GENERAL.FORM-VALIDATION-DISPLAY-DIALOG.HAS-ERROR') + ', ' + formControl.errors[key].message); }
});
return errors;
}
private _getPlaceHolder(formControl: any): string {
if (formControl.nativeElement.localName === 'input' || formControl.nativeElement.localName === 'textarea') {
return formControl.nativeElement.getAttribute('placeholder');
} else if (formControl.nativeElement.localName === 'mat-select') {
return formControl.nativeElement.getAttribute('aria-label');
} else if (formControl.nativeElement.localName === 'app-single-auto-complete') {
return (Array.from(formControl.nativeElement.firstChild.children).filter((x: any) => x.localName === 'input')[0] as any).getAttribute('placeholder');
} else if (formControl.nativeElement.localName === 'app-multiple-auto-complete') {
return (Array.from(formControl.nativeElement.firstChild.children).filter((x: any) => x.localName === 'input')[0] as any).getAttribute('placeholder');
}
}
private _buildDMPDescriptionErrorMessages(): string[]{//not including datasets
const errmess: string[] = [];
Object.keys(this.formGroup.controls).forEach(controlName=>{
if(controlName != 'datasets' && this.formGroup.get(controlName).invalid){
errmess.push(...this._buildErrorMessagesForAbstractControl(this.formGroup.get(controlName), controlName));
}
})
return errmess;
}
// takes as an input an abstract control and gets its error messages[]
private _buildErrorMessagesForAbstractControl(aControl: AbstractControl, controlName: string):string[]{
const errmess:string[] = [];
if(aControl.invalid){
if(aControl.errors){
//check if has placeholder
if( (<any>aControl).nativeElement !== undefined && (<any>aControl).nativeElement !== null){
controlName = this._getPlaceHolder(aControl);
}
const errorMessage = this._getErrorMessage(aControl, controlName);
errmess.push(...errorMessage);
}
/*in case the aControl is FormControl then the it should have provided its error messages above.
No need to check case of FormControl below*/
if(aControl instanceof FormGroup){
const fg = aControl as FormGroup;
//check children
Object.keys(fg.controls).forEach(controlName=>{
errmess.push(...this._buildErrorMessagesForAbstractControl(fg.get(controlName), controlName));
});
}else if(aControl instanceof FormArray){
const fa = aControl as FormArray;
fa.controls.forEach((control,index)=>{
errmess.push(... this._buildErrorMessagesForAbstractControl(control, `${controlName} --> ${index+1}`));
});
}
}
return errmess;
}
addDataset() {
this.formSubmit(true, false);
if(!this._isDMPDescriptionValid()){
const errmess = this._buildDMPDescriptionErrorMessages();
this.showValidationErrorsDialog(undefined, errmess);
return;
}
this.onSubmit(true, false);
// this.formSubmit(true, false);
// Add dataset to list
// if (!this.formGroup.get('datasets')) {
// this.formGroup.addControl('datasets', new FormBuilder().array(new Array<FormControl>()));

View File

@ -10,9 +10,13 @@
[ngStyle]="calculateStyle(entry)"
[ngClass]="calculateClass(entry)"
>
<span>
<span [class.text-danger]="showErrors && entry.form.invalid && entry.type === tocEntryTypeEnum.FieldSet">
{{entry.numbering}}. {{entry.label}}
</span>
<mat-icon style="transform: translateY(3px);" class="text-danger"
*ngIf="showErrors && entry.form.invalid && entry.type !== tocEntryTypeEnum.FieldSet && !expandChildren[idx]">
priority_high
</mat-icon>
<!-- <ng-container *ngIf="entry.subEntries && entry.subEntries.length && !expandChildren[idx]">
<small>
({{entry.subEntries.length}})
@ -28,7 +32,8 @@
*ngIf="entry.subEntries && entry.subEntries.length && expandChildren[idx]"
(entrySelected)="onEntrySelected($event)"
[selected]="selected"
[TOCENTRY_ID_PREFIX]="TOCENTRY_ID_PREFIX">
[TOCENTRY_ID_PREFIX]="TOCENTRY_ID_PREFIX"
[showErrors]="showErrors">
</table-of-contents-internal>
</div>

View File

@ -2,6 +2,7 @@
.internal-table{
margin-left: 1em;
padding-left: 0.2rem;
}
.table-entry{
@ -26,3 +27,7 @@
font-weight: 700 !important;
opacity: 1 !important;
}
.section{
line-height: 1.7em;
}

View File

@ -24,6 +24,7 @@ export class TableOfContentsInternal implements OnInit {
expandChildren:boolean[];
tocEntryTypeEnum = ToCEntryType;
@Input() TOCENTRY_ID_PREFIX="";
@Input() showErrors: boolean = false;
constructor(public visibilityRulesService: VisibilityRulesService){
@ -84,11 +85,16 @@ export class TableOfContentsInternal implements OnInit {
}
calculateClass(entry:ToCEntry){
const myClass= {};
if(this.selected && entry.id === this.selected.id){
return{'selected': true};
myClass['selected'] = true;
}
return {};
if(entry.type != this.tocEntryTypeEnum.FieldSet){
myClass['section'] = true;
}
return myClass;
}
}

View File

@ -28,6 +28,7 @@
<div class="scroll-container col-12 internal-table">
<table-of-contents-internal [TOCENTRY_ID_PREFIX]="TOCENTRY_ID_PREFIX" [tocentries]="tocentries"
[showErrors]="showErrors"
(entrySelected)="onToCentrySelected($event)"
[selected]="tocentrySelected"

View File

@ -4,9 +4,10 @@ import {TableOfContents} from './table-of-contents';
import {RouterModule} from '@angular/router';
import { TableOfContentsInternal } from './table-of-contents-internal/table-of-contents-internal';
import { VisibilityRulesService } from '../visibility-rules/visibility-rules.service';
import { MatIconModule } from '@angular/material';
@NgModule({
imports: [CommonModule, RouterModule],
imports: [CommonModule, RouterModule, MatIconModule],
declarations: [TableOfContents, TableOfContentsInternal],
exports: [TableOfContents],
entryComponents: [TableOfContents],

View File

@ -48,6 +48,7 @@ export class TableOfContents extends BaseComponent implements OnInit {
@Input() TOCENTRY_ID_PREFIX = '';
// visibilityRules:Rule[] = [];
@Input() visibilityRules:Rule[] = [];
@Input() showErrors: boolean = false;
private _tocentrySelected:ToCEntry = null;
get tocentrySelected(){