From 70a99f0d238a059b0574b275ab87eee5c9c973bf Mon Sep 17 00:00:00 2001 From: Kristan Ntavidi Date: Mon, 1 Mar 2021 18:42:18 +0200 Subject: [PATCH] Dataset Profile Editor. Error messages before each step --- .../admin/view-style-editor-model.ts | 4 +- ...rofile-editor-composite-field.component.ts | 12 +- ...ataset-profile-editor-field.component.html | 10 +- .../dataset-profile-editor-field.component.ts | 37 ++- ...ofile-editor-section-fieldset.component.ts | 32 +++ .../dataset-profile-editor.component.html | 21 +- .../dataset-profile-editor.component.ts | 267 +++++++++++++----- .../table-of-contents/table-of-contents.ts | 9 + 8 files changed, 308 insertions(+), 84 deletions(-) diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/admin/view-style-editor-model.ts b/dmp-frontend/src/app/ui/admin/dataset-profile/admin/view-style-editor-model.ts index de61824a9..586d603f8 100644 --- a/dmp-frontend/src/app/ui/admin/dataset-profile/admin/view-style-editor-model.ts +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/admin/view-style-editor-model.ts @@ -1,4 +1,4 @@ -import { FormGroup } from "@angular/forms"; +import { FormGroup, Validators } from "@angular/forms"; import { ViewStyle } from "../../../../core/model/admin/dataset-profile/dataset-profile"; import { BaseFormModel } from "../../../../core/model/base-form-model"; @@ -15,7 +15,7 @@ export class ViewStyleEditorModel extends BaseFormModel { buildForm(disabled: boolean = false, skipDisable: Array = []): FormGroup { const formGroup = this.formBuilder.group({ cssClass: [{ value: this.cssClass, disabled: (disabled && !skipDisable.includes('ViewStyleEditorModel.cssClass')) }], - renderStyle: [{ value: this.renderStyle, disabled: (disabled && !skipDisable.includes('ViewStyleEditorModel.renderStyle')) }] + renderStyle: [{ value: this.renderStyle, disabled: (disabled && !skipDisable.includes('ViewStyleEditorModel.renderStyle')) }, Validators.required] }); return formGroup; } diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/composite-field/dataset-profile-editor-composite-field.component.ts b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/composite-field/dataset-profile-editor-composite-field.component.ts index 3b4b8439c..15512ac83 100644 --- a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/composite-field/dataset-profile-editor-composite-field.component.ts +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/composite-field/dataset-profile-editor-composite-field.component.ts @@ -184,12 +184,14 @@ export class DatasetProfileEditorCompositeFieldComponent implements OnInit, OnCh field.ordinal = (this.form.get('fields') as FormArray).length; const fieldForm = field.buildForm(); - fieldForm.setValidators(this.customFieldValidator()); + // fieldForm.setValidators(this.customFieldValidator()); + // fieldForm.get('viewStyle').get('renderStyle').setValidators(Validators.required); (this.form.get('fields')).push(fieldForm); + this.setTargetField(fieldForm); - + fieldForm.updateValueAndValidity(); } DeleteField(index) { @@ -331,9 +333,9 @@ export class DatasetProfileEditorCompositeFieldComponent implements OnInit, OnCh field.ordinal = (this.form.get('fields') as FormArray).length; const fieldForm = field.buildForm(); - fieldForm.setValidators(this.customFieldValidator()); + // fieldForm.setValidators(this.customFieldValidator()); // fieldForm.get('viewStyle').get('renderStyle').setValidators(Validators.required); - + if (fieldForm.get('data')) { @@ -423,6 +425,8 @@ export class DatasetProfileEditorCompositeFieldComponent implements OnInit, OnCh (this.form.get('fields')).push(fieldForm); + // fieldForm.updateValueAndValidity(); + } private customFieldValidator(): ValidatorFn{ diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field/dataset-profile-editor-field.component.html b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field/dataset-profile-editor-field.component.html index 62a5afe03..bb8d3ac52 100644 --- a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field/dataset-profile-editor-field.component.html +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field/dataset-profile-editor-field.component.html @@ -70,6 +70,7 @@ {{enumUtils.toDatasetProfileViewTypeString(viewTypeEnum.TextArea)}} @@ -107,8 +108,9 @@ - {{'GENERAL.VALIDATION.REQUIRED' | translate}} - {{'GENERAL.VALIDATION.REQUIRED' | translate}} + + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + @@ -190,7 +192,7 @@ - +
{{'DATASET-PROFILE-EDITOR.ACTIONS.FIELD.PREVIEW' | translate}} @@ -213,4 +215,4 @@
-{{form.touched|json}} \ No newline at end of file + \ No newline at end of file diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field/dataset-profile-editor-field.component.ts b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field/dataset-profile-editor-field.component.ts index c53c1f0c6..7b44cf852 100644 --- a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field/dataset-profile-editor-field.component.ts +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field/dataset-profile-editor-field.component.ts @@ -41,7 +41,7 @@ import { ErrorStateMatcher, MatSlideToggleChange } from '@angular/material'; templateUrl: './dataset-profile-editor-field.component.html', styleUrls: ['./dataset-profile-editor-field.component.scss'] }) -export class DatasetProfileEditorFieldComponent extends BaseComponent implements OnInit { +export class DatasetProfileEditorFieldComponent extends BaseComponent implements OnInit, ErrorStateMatcher { @Input() viewOnly: boolean; @Input() form: FormGroup; @Input() showOrdinal = true; @@ -52,6 +52,7 @@ export class DatasetProfileEditorFieldComponent extends BaseComponent implements viewType: ViewStyleType; viewTypeEnum = ViewStyleType; + // matcher:MyErrorStateMatcher = new MyErrorStateMatcher(); @Input() expandView: boolean = true; @@ -64,6 +65,14 @@ export class DatasetProfileEditorFieldComponent extends BaseComponent implements public datasetProfileService: DatasetProfileService ) { super(); } + + isErrorState(control: FormControl, form: FormGroupDirective | NgForm): boolean { + + if(this.form.get('viewStyle').untouched) return false; + + return this.form.get('viewStyle').invalid; + } + ngOnInit() { this.showPreview = true; if (this.form.get('multiplicity')) { @@ -73,6 +82,8 @@ export class DatasetProfileEditorFieldComponent extends BaseComponent implements } if(this.form.get('viewStyle').get('renderStyle').value){ + + // this.matcher.setReference(this.form); const type = this.form.get('viewStyle').get('renderStyle').value; switch(type){ @@ -502,4 +513,26 @@ export class DatasetProfileEditorFieldComponent extends BaseComponent implements this.delete.emit(); } -} \ No newline at end of file +} + +// class MyErrorStateMatcher implements ErrorStateMatcher { + +// reference: AbstractControl; + +// setReference(acontrol: AbstractControl){ +// this.reference = acontrol; +// } + +// isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean { + + +// if(this.reference){ + +// if(this.reference.valid) return false; + +// return this.reference.touched && this.reference.invalid; +// } + +// return true; +// } +// } \ No newline at end of file diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/section-fieldset/dataset-profile-editor-section-fieldset.component.ts b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/section-fieldset/dataset-profile-editor-section-fieldset.component.ts index 86f16d977..d3a36d010 100644 --- a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/section-fieldset/dataset-profile-editor-section-fieldset.component.ts +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/section-fieldset/dataset-profile-editor-section-fieldset.component.ts @@ -29,6 +29,9 @@ export class DatasetProfileEditorSectionFieldSetComponent extends BaseComponent @Output() selectedEntryId = new EventEmitter(); @Output() dataNeedsRefresh = new EventEmitter (); + + // FIELDSET_PREFIX_ID="FIELDSET_PREFIX_ID"; + // @Output() fieldsetAdded = new EventEmitter(); //returns the id of the fieldset added idprefix = "id"; private subs = new Subscription(); @@ -53,6 +56,14 @@ export class DatasetProfileEditorSectionFieldSetComponent extends BaseComponent } }); this.subs.add(this.dragulaService.drop(this.FIELDSETS).subscribe(obs=>{ + + + (this.form.get('fieldSets') as FormArray).controls.forEach((e,i)=>{ + e.get('ordinal').setValue(i); + }); + + + // obs. this.dataNeedsRefresh.emit(); return ; })); @@ -225,4 +236,25 @@ export class DatasetProfileEditorSectionFieldSetComponent extends BaseComponent // if (formGroup.get('title') && formGroup.get('title').value && formGroup.get('title').value.length > 0) { return formGroup.get('title').value; } // return "Field " + (index + 1); // } + private _findTocEntryById(id: string, tocentries: ToCEntry[]): ToCEntry{ + if(!tocentries){ + return null; + } + + let tocEntryFound = tocentries.find(entry=>entry.id === id); + + if(tocEntryFound){ + return tocEntryFound; + } + + for(let entry of tocentries){ + const result = this._findTocEntryById(id, entry.subEntries); + if(result){ + tocEntryFound = result; + break; + } + } + + return tocEntryFound? tocEntryFound: null; + } } diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/dataset-profile-editor.component.html b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/dataset-profile-editor.component.html index 0baa664b4..38640a9eb 100644 --- a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/dataset-profile-editor.component.html +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/dataset-profile-editor.component.html @@ -41,7 +41,7 @@ - @@ -317,9 +317,14 @@
- + + + + + + @@ -351,17 +356,17 @@ -
+ + diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/dataset-profile-editor.component.ts b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/dataset-profile-editor.component.ts index fe4092a17..513930b1f 100644 --- a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/dataset-profile-editor.component.ts +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/dataset-profile-editor.component.ts @@ -39,6 +39,7 @@ import { CdkStep, StepperSelectionEvent } from '@angular/cdk/stepper'; import { DatasetDescriptionCompositeFieldEditorModel, DatasetDescriptionFieldEditorModel, DatasetDescriptionFormEditorModel, DatasetDescriptionPageEditorModel, DatasetDescriptionSectionEditorModel } from '@app/ui/misc/dataset-description-form/dataset-description-form.model'; import { Rule } from '@app/core/model/dataset-profile-definition/rule'; import { DatasetProfileFieldViewStyle } from '@app/core/common/enum/dataset-profile-field-view-style'; +import { invalid } from '@angular/compiler/src/render3/view/util'; const skipDisable: any[] = require('../../../../../assets/resources/skipDisable.json'); @Component({ @@ -756,7 +757,7 @@ export class DatasetProfileEditorComponent extends BaseComponent implements OnIn field.id = Guid.create().toString(); field.ordinal = 0;//first filed in the fields list const fieldForm = field.buildForm(); - fieldForm.setValidators(this.customFieldValidator()); + // fieldForm.setValidators(this.customFieldValidator()); // fieldForm.get('viewStyle').get('renderStyle').setValidators(Validators.required); // fieldSet.fields.push(field); @@ -778,6 +779,7 @@ export class DatasetProfileEditorComponent extends BaseComponent implements OnIn this.refreshToCEntries(); this.selectedTocEntry = this._findTocEntryById(fieldSetId, this.toCEntries); + // fieldForm.updateValueAndValidity(); break; @@ -1390,7 +1392,7 @@ export class DatasetProfileEditorComponent extends BaseComponent implements OnIn validateStep(step: CdkStep){ if(step.hasError){ - this.checkFormValidation(); + this.printMyErrors(); } } @@ -1413,54 +1415,161 @@ export class DatasetProfileEditorComponent extends BaseComponent implements OnIn + private _buildErrorMessage(errors, numbering, key):string[]{ + const errmess: string[] = []; + + Object.keys(errors).forEach(keyError => { + if (typeof errors[keyError] === 'boolean') { + errmess.push(numbering + ' ' + key + ' is ' + keyError); + } else { + errmess.push(numbering + ' ' + key + ': ' + keyError + ': ' + JSON.stringify(errors[keyError])); + } + }); + + return errmess; + } + printMyErrors(){ // this._printToCentriesErrrors(this.toCEntries); - this._getErrors(this.form); + const result = this._getErrors(this.form); + // console.log('result', result); + + + // console.log(result); + + if(result){ + + this.form.markAllAsTouched(); + const errmess:string[] = []; + + let indexes:number[] = []; + ///search in pages,sections and fieldsets for the id + result.forEach((err,i)=>{ + const entry = this._findTocEntryById(err.id, this.toCEntries); + if(entry){ + + // errmess.push(`Error on ${entry.numbering} ${entry.label} . ${err.key}`); + errmess.push(...this._buildErrorMessage(err.errors, entry.numbering, err.key)); + indexes.push(i); + } + }); + indexes.reverse().forEach(index=>{ + result.splice(index,1); + }); + + indexes = []; + //searching in fields + const fieldsets = this._getAllFieldSets(this.toCEntries); + result.forEach((err,i)=>{ + fieldsets.filter(fs=>{ + let fieldFound = false; + (fs.form.get('fields') as FormArray).controls.forEach(field=>{ + if(field.get('id').value === err.id){ + fieldFound = true; + indexes.push(i); + } + }); + return fieldFound; + }) + //printing fieldsets that the field missing + .forEach(fs=>{ + // errmess.push(`Missing input in ${fs.numbering} ${fs.label} . ${err.key}`); + errmess.push(...this._buildErrorMessage(err.errors, fs.numbering, err.key)); + }); + }); + + indexes.reverse().forEach(index=>{ + result.splice(index,1); + }); + + result.forEach(err=>{ + errmess.push(`Missing ${err.key}` ); + }) + + // console.log(result); + + + if(this.stepper.selectedIndex != 0){ + if(!(this.form.get('pages') as FormArray).length){ + errmess.push( + 'There has to be at least one section in the template.' + ); + } + } + + + const dialogRef = this.dialog.open(FormValidationErrorsDialogComponent, { + disableClose: true, + autoFocus: false, + restoreFocus: false, + data: { + errorMessages: errmess, + projectOnly: false + }, + }); + } } - - private _printToCentriesErrrors(entries: ToCEntry[]){ + private _getAllFieldSets(entries: ToCEntry[]):ToCEntry[]{ - if(!entries || !entries.length) return; + const fieldsets:ToCEntry[] = []; + if(!entries || !entries.length) return fieldsets; + entries.forEach(e=>{ - - - // if(e.form instanceof FormGroup) console.log('is Formgroup'); - // if(e.form instanceof FormControl) console.log('is formcontrols'); - // if(e.form instanceof FormArray) console.log('isForm array'); - - - const form = e.form as FormGroup; - if(form.invalid){ - const controls = form.controls; - const keys = Object.keys(controls); - keys.forEach(key=>{ - const control = controls[key]; - - if(control.invalid){ - console.log('control ', key, 'is invalid'); - } - }) - - - // console.log('keys,', keys); - - - this._printToCentriesErrrors(e.subEntries); + if(e.type === ToCEntryType.FieldSet){ + fieldsets.push(e); + }else{ + fieldsets.push(...this._getAllFieldSets(e.subEntries)); } }); + return fieldsets; } + // private _printToCentriesErrrors(entries: ToCEntry[]){ + + // if(!entries || !entries.length) return; + + // entries.forEach(e=>{ + + + // // if(e.form instanceof FormGroup) console.log('is Formgroup'); + // // if(e.form instanceof FormControl) console.log('is formcontrols'); + // // if(e.form instanceof FormArray) console.log('isForm array'); + + + // const form = e.form as FormGroup; + // if(form.invalid){ + // const controls = form.controls; + // const keys = Object.keys(controls); + // keys.forEach(key=>{ + // const control = controls[key]; + + // if(control.invalid){ + // console.log('control ', key, 'is invalid'); + // } + // }) + + + // // console.log('keys,', keys); + + + // this._printToCentriesErrrors(e.subEntries); + // } + // }); + // } - private _getErrors(aControl: AbstractControl){ - if(aControl instanceof FormGroup) console.log('is Formgroup'); - if(aControl instanceof FormControl)console.log( aControl.errors); - if(aControl instanceof FormArray) console.log('isForm array'); + private _getErrors(aControl: AbstractControl):InvalidControl[]{ + + // if(aControl instanceof FormGroup) console.log('is Formgroup'); + // if(aControl instanceof FormControl)console.log( aControl.errors); + // if(aControl instanceof FormArray) console.log('isForm array'); + + // console if(aControl.valid) return; let controlType = 'control'; @@ -1468,49 +1577,79 @@ export class DatasetProfileEditorComponent extends BaseComponent implements OnIn if(aControl instanceof FormGroup) controlType="fg" if(aControl instanceof FormControl) controlType="fc"; if(aControl instanceof FormArray) controlType="fa"; + - + const invalidControls:InvalidControl[] = []; //invalid switch (controlType){ case 'fg': + + const controls = (aControl as FormGroup).controls; + const keys = Object.keys(controls); + keys.forEach(key=>{ + const control = controls[key]; + if(control.valid) return; - const controls = (aControl as FormGroup).controls; - const keys = Object.keys(controls); - keys.forEach(key=>{ - const control = controls[key]; - // if(control.invalid){ - this._getErrors(control); - // } - }); + if(control instanceof FormControl){ + const ctrl = control as FormControl; + + // console.log('Control ', key,' of Formgroup ', aControl.get('id'), ' has errors', ctrl.errors); + // console.log(aControl) - break; - case 'fc': - //form control print errors - console.log(aControl.errors); + invalidControls.push({ + errors:ctrl.errors, + id: ctrl.get('id')? ctrl.get('id').value: null, + invalidSubControls: [], + key: key + }); + + }else{ + + invalidControls.push(...this._getErrors(control)) ; + + } + }); break; case 'fa': - const fa = (aControl as FormArray); - fa.controls.forEach(control=>{ - this._getErrors(control); + // const fa = (aControl as FormArray); + const ctrls = (aControl as FormArray).controls; + const keys_ = Object.keys(ctrls); + keys_.forEach(key=>{ + const control = ctrls[key]; + if(control.valid) return; + + + if(control instanceof FormControl){ //for completion purposes. should never run this case + const ctrl = control as FormControl; + invalidControls.push({ + errors:ctrl.errors, + id: ctrl.get('id')? ctrl.get('id').value: null, + invalidSubControls: [], + key: key + }); + + }else{ + invalidControls.push(... this._getErrors(control)); + } }); + break; } - - - - - // const controls = rootForm.controls; - // const keys = Object.keys(controls); - // keys.forEach(key=>{ - // const control = controls[key]; - - - // if(control.invalid){ - // console.log('control ', key, 'is invalid'); - // } - // }) + invalidControls.forEach(e=>{ + if(!e.id){ + e.id = aControl.get('id')? aControl.get('id').value : null; + } + }) + return invalidControls; } +} + +interface InvalidControl{ + key: string, + errors: any, + id: string, + invalidSubControls: InvalidControl[] } \ No newline at end of file diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/table-of-contents/table-of-contents.ts b/dmp-frontend/src/app/ui/admin/dataset-profile/table-of-contents/table-of-contents.ts index 684b911a4..c3494bd2c 100644 --- a/dmp-frontend/src/app/ui/admin/dataset-profile/table-of-contents/table-of-contents.ts +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/table-of-contents/table-of-contents.ts @@ -434,9 +434,18 @@ export class DatasetProfileTableOfContents extends BaseComponent implements OnIn } }); drake.on('dragend',(el)=>{ + const draggingItem = this.draggingItemId; this.isDragging = false; this.draggingItemId = null; this.overcontainer = null; + + + const entry = this._findTocEntryById(draggingItem, this.links); + + if(entry){ + this.itemClicked(entry); + } + });