Dataset Profile Editor. Error messages before each step

This commit is contained in:
Kristian Ntavidi 2021-03-01 18:42:18 +02:00
parent 081995e243
commit 70a99f0d23
8 changed files with 308 additions and 84 deletions

View File

@ -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<String> = []): 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;
}

View File

@ -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);
(<FormArray>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
(<FormArray>this.form.get('fields')).push(fieldForm);
// fieldForm.updateValueAndValidity();
}
private customFieldValidator(): ValidatorFn{

View File

@ -70,6 +70,7 @@
<!-- NEW VERSION -->
<mat-select placeholder="{{'DATASET-PROFILE-EDITOR.STEPS.FORM.FIELD.FIELDS.VIEW-STYLE' | translate}}" [(ngModel)]="viewType" (selectionChange)="onInputTypeChange()"
[disabled]="viewOnly"
[errorStateMatcher]="this"
required
>
<mat-option [value]="viewTypeEnum.TextArea">{{enumUtils.toDatasetProfileViewTypeString(viewTypeEnum.TextArea)}}</mat-option>
@ -107,8 +108,9 @@
</mat-optgroup>
</mat-select>
<mat-error *ngIf="this.form.get('viewStyle').get('renderStyle').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
<mat-error *ngIf="this.form.hasError('inputTypeNotValid')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
<!-- <mat-error *ngIf="this.form.get('viewStyle').get('renderStyle').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error> -->
<mat-error >{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
<!-- <mat-error *ngIf="this.form.hasError('inputTypeNotValid')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error> -->
</mat-form-field>
@ -190,7 +192,7 @@
</ng-container>
<ng-container>
<ng-container *ngIf="true">
<div class="row">
<div class="col-12" *ngIf="expandView && previewForm">
<span style="font-weight: bold">{{'DATASET-PROFILE-EDITOR.ACTIONS.FIELD.PREVIEW' | translate}}</span>
@ -213,4 +215,4 @@
</div>
</div>
</ng-container>
{{form.touched|json}}
<!-- {{form.touched|json}} -->

View File

@ -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();
}
}
}
// 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;
// }
// }

View File

@ -29,6 +29,9 @@ export class DatasetProfileEditorSectionFieldSetComponent extends BaseComponent
@Output() selectedEntryId = new EventEmitter<string>();
@Output() dataNeedsRefresh = new EventEmitter<void> ();
// FIELDSET_PREFIX_ID="FIELDSET_PREFIX_ID";
// @Output() fieldsetAdded = new EventEmitter<String>(); //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;
}
}

View File

@ -41,7 +41,7 @@
<button mat-button class="navigate-btn" (click)="stepper.previous()" *ngIf="stepper.selectedIndex !=0">
<mat-icon>navigate_before</mat-icon> {{'DMP-EDITOR.STEPPER.PREVIOUS' | translate}}
</button>
<button mat-button class="navigate-btn ml-2" (click)="validateStep(stepper.selected);stepper.next();" *ngIf="stepper.selectedIndex != (steps.length-1)">
<button mat-button class="navigate-btn ml-2" (click)="stepper.next();validateStep(stepper.selected);" *ngIf="stepper.selectedIndex != (steps.length-1)">
{{'DMP-EDITOR.STEPPER.NEXT' | translate}}<mat-icon>navigate_next</mat-icon>
</button>
@ -317,9 +317,14 @@
<!-- SAVE BUTTON -->
<div class="col-6 d-flex" *ngIf="!viewOnly">
<div class="row mt-4">
<button mat-raised-button class="col-auto mr-2" color="primary" type="button col-auto"
(click)='checkFormValidation()'
[disabled]="form.valid">{{'DATASET-PROFILE-EDITOR.ACTIONS.VALIDATE' | translate}}</button>
<ng-container *ngIf="false">
<button mat-raised-button class="col-auto mr-2" color="primary" type="button col-auto"
(click)='checkFormValidation()'
[disabled]="form.valid">{{'DATASET-PROFILE-EDITOR.ACTIONS.VALIDATE' | translate}}</button>
</ng-container>
<button mat-raised-button class="col-auto mr-2" color="primary" type="button col-auto"
(click)='onSubmit()' [disabled]="!form.valid">{{'DATASET-PROFILE-EDITOR.ACTIONS.SAVE' |
translate}}</button>
@ -351,17 +356,17 @@
</ng-container>
<div class="row">
<!-- <div class="row">
<button (click)="printForm()">
console form
</button>
</div>
<div class="row">
</div> -->
<!-- <div class="row">
<button (click)="printMyErrors()">
print errors
</button>
</div>
</div> -->
<!-- <div class="row">
<button (click)="foo()">foo</button>
</div> -->

View File

@ -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[]
}

View File

@ -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);
}
});