Dataset ProfileEditor Validator fixes. More validators added. Table of contents mark invalid entries. Guided tour fix. Fix ng-deep leaks from user listing.

This commit is contained in:
Kristian Ntavidi 2021-03-12 18:19:51 +02:00
parent 615d30b977
commit 4d6e4fb2cd
18 changed files with 418 additions and 192 deletions

View File

@ -64,12 +64,31 @@ export class AppComponent implements OnInit, AfterViewInit {
ngAfterViewInit(): void {
setTimeout(() => {
this.sideNavSubscription = this.sidenavService.status().subscribe(isopen=>{
const hamburger = document.getElementById('hamburger');
if(isopen){
//update value of hamburfer
document.getElementById('hamburger').classList.add('change');
if(!hamburger){//try later
setTimeout(() => {
const hamburger = document.getElementById('hamburger');
if(hamburger){
hamburger.classList.add('change');
}
}, 300);
}else{
hamburger.classList.add('change');
}
this.sidenav.open()
}else{
document.getElementById('hamburger').classList.remove('change');
}else{//closed
if(!hamburger){//try later
setTimeout(() => {
const hamburger = document.getElementById('hamburger');
if(hamburger){
hamburger.classList.remove('change');
}
}, 300);
}else{
hamburger.classList.remove('change');
}
this.sidenav.close();
}

View File

@ -388,7 +388,10 @@ export class GuidedTourComponent implements AfterViewInit, OnDestroy {
}
const scrollAdjustment = this.currentTourStep.scrollAdjustment ? this.currentTourStep.scrollAdjustment : 0;
const tourStepHeight = typeof this.tourStep.nativeElement.getBoundingClientRect === 'function' ? this.tourStep.nativeElement.getBoundingClientRect().height : 0;
let tourStepHeight = 0;
if (this.tourStep != null && this.tourStep.nativeElement != null && typeof this.tourStep.nativeElement.getBoundingClientRect === 'function') {
tourStepHeight = this.tourStep.nativeElement.getBoundingClientRect().height;
}
const elementHeight = this.selectedElementRect.height + scrollAdjustment + tourStepHeight;
if ((this.windowRef.nativeWindow.innerHeight - this.topOfPageAdjustment) < elementHeight) {

View File

@ -67,12 +67,12 @@
</div>
</div> -->
<mat-form-field appearance="none" class="numbering-label" [ngStyle]="calculateLabelWidth(numbering)">
<input matInput type="text" [value]="numbering" disabled>
<input [ngClass]="{'text-danger':form.get('title').invalid &&form.get('title').touched}" matInput type="text" [value]="numbering" disabled>
</mat-form-field>
<!-- [appearance]="titleControl.focused? 'legacy':'none'" floatLabel="never" -->
<mat-form-field class="col field-title" [appearance]="'none'">
<mat-form-field class="col field-title" [appearance]="'none'" floatLabel="never">
<input matInput type="text" placeholder="Title" #titleControl="matInput"
[formControl]="this.form.get('title')">
[formControl]="this.form.get('title')" required>
</mat-form-field>
</div>
</div>
@ -145,6 +145,7 @@
[form]="form.get('fields').get(''+i)" [showOrdinal]="false"
[indexPath]="indexPath + 'f' + i" [viewOnly]="viewOnly"
[expandView]="hasFocus"
[canBeDeleted]="form.get('fields')['controls'].length !=1"
(delete)="deleteField(i)">
</app-dataset-profile-editor-field-component>
</div>

View File

@ -32,6 +32,7 @@ import { CurrencyDataEditorModel } from '../../../admin/field-data/currency-data
import { ValidationDataEditorModel } from '../../../admin/field-data/validation-data-editor-models';
import { DatasetProfileService } from '@app/core/services/dataset-profile/dataset-profile.service';
import { OrganizationsDataEditorModel } from '../../../admin/field-data/organizations-data-editor-models';
import { EditorCustomValidators } from '../../custom-validators/editor-custom-validators';
@Component({
selector: 'app-dataset-profile-editor-composite-field-component',
@ -59,6 +60,7 @@ export class DatasetProfileEditorCompositeFieldComponent implements OnInit, OnCh
viewStyleEnum = DatasetProfileFieldViewStyle;
viewTypeEnum = ViewStyleType;
private myCustomValidators:EditorCustomValidators = new EditorCustomValidators();
constructor(
private dialog: MatDialog,
@ -358,15 +360,15 @@ export class DatasetProfileEditorCompositeFieldComponent implements OnInit, OnCh
fieldForm.get('viewStyle').get('renderStyle').setValue(DatasetProfileFieldViewStyle.ComboBox)
fieldForm.addControl('data', new WordListFieldDataEditorModel().buildForm());
this.form.get('data').setValidators(this._atLeastOneElementListValidator('options'));
this.form.get('data').updateValueAndValidity();
fieldForm.get('data').setValidators(this.myCustomValidators.atLeastOneElementListValidator('options'));
fieldForm.get('data').updateValueAndValidity();
break;
case this.viewTypeEnum.Other:
fieldForm.get('viewStyle').get('renderStyle').setValue(DatasetProfileFieldViewStyle.ComboBox)
fieldForm.addControl('data', new AutoCompleteFieldDataEditorModel().buildForm()); //TODO SEE
fieldForm.get('data').setValidators(this._atLeastOneElementListValidator('autoCompleteSingleDataList'));
fieldForm.get('data').setValidators(this.myCustomValidators.atLeastOneElementListValidator('autoCompleteSingleDataList'));
fieldForm.get('data').updateValueAndValidity();
break;
case this.viewTypeEnum.InternalDmpEntities:
@ -380,6 +382,9 @@ export class DatasetProfileEditorCompositeFieldComponent implements OnInit, OnCh
case this.viewTypeEnum.RadioBox:
fieldForm.get('viewStyle').get('renderStyle').setValue(DatasetProfileFieldViewStyle.RadioBox)
fieldForm.addControl('data', new RadioBoxFieldDataEditorModel().buildForm());
fieldForm.get('data').setValidators(this.myCustomValidators.atLeastOneElementListValidator('options'));
fieldForm.get('data').updateValueAndValidity();
break;
case this.viewTypeEnum.TextArea:
fieldForm.get('viewStyle').get('renderStyle').setValue(DatasetProfileFieldViewStyle.TextArea)
@ -416,7 +421,7 @@ export class DatasetProfileEditorCompositeFieldComponent implements OnInit, OnCh
break;
case this.viewTypeEnum.Organizations:
fieldForm.get('viewStyle').get('renderStyle').setValue(DatasetProfileFieldViewStyle.Organizations)
this.form.addControl('data', new OrganizationsDataEditorModel().buildForm());
fieldForm.addControl('data', new OrganizationsDataEditorModel().buildForm());
// this.form.addControl('data', new OrganizationsDataEditorModel().buildForm())
// fieldForm.addControl('data', new DatasetsAutoCompleteFieldDataEditorModel().buildForm()); //TODO
break;
@ -436,7 +441,9 @@ export class DatasetProfileEditorCompositeFieldComponent implements OnInit, OnCh
(<FormArray>this.form.get('fields')).push(fieldForm);
// fieldForm.updateValueAndValidity();
fieldForm.get('viewStyle').get('renderStyle').updateValueAndValidity();
fieldForm.get('data').updateValueAndValidity();
}
@ -488,17 +495,17 @@ export class DatasetProfileEditorCompositeFieldComponent implements OnInit, OnCh
// }
private _atLeastOneElementListValidator(arrayToCheck): ValidatorFn{
return (control: AbstractControl): ValidationErrors | null=>{
// private _atLeastOneElementListValidator(arrayToCheck): ValidatorFn{
// return (control: AbstractControl): ValidationErrors | null=>{
const fa = control.get(arrayToCheck) as FormArray;
// const fa = control.get(arrayToCheck) as FormArray;
if(fa.length === 0){
return {emptyArray: true};
}
return null;
}
}
// if(fa.length === 0){
// return {emptyArray: true};
// }
// return null;
// }
// }
calculateLabelWidth(numbering: string){

View File

@ -12,7 +12,7 @@
<li class="list-inline-item" *ngIf="!viewOnly && viewType">
<mat-icon style="cursor: pointer;" (click)="addNewRule()" [matTooltip]="'DATASET-PROFILE-EDITOR.ACTIONS.FIELD.ADD-VISIBILITY-RULE' | translate">visibility</mat-icon>
</li>
<li class="list-inline-item" *ngIf="!viewOnly && viewType">
<li class="list-inline-item" *ngIf="!viewOnly && viewType && canBeDeleted">
<mat-icon style="cursor: pointer;" (click)="onDelete()" [matTooltip]="'DATASET-PROFILE-EDITOR.ACTIONS.FIELD.DELETE-INPUT' | translate">delete</mat-icon>
</li>

View File

@ -60,7 +60,7 @@ export class DatasetProfileEditorFieldComponent extends BaseComponent implements
@Input() expandView: boolean = true;
@Input() canBeDeleted:boolean = true;
@Output() delete = new EventEmitter<void>();
@ -425,7 +425,7 @@ export class DatasetProfileEditorFieldComponent extends BaseComponent implements
this.form.get('viewStyle').get('renderStyle').setValue(DatasetProfileFieldViewStyle.ComboBox)
this.form.addControl('data', new WordListFieldDataEditorModel().buildForm());
this.form.get('data').setValidators(this.myCustomValidators._atLeastOneElementListValidator('options'));
this.form.get('data').setValidators(this.myCustomValidators.atLeastOneElementListValidator('options'));
this.form.get('data').updateValueAndValidity();
break;
@ -433,7 +433,7 @@ export class DatasetProfileEditorFieldComponent extends BaseComponent implements
this.form.get('viewStyle').get('renderStyle').setValue(DatasetProfileFieldViewStyle.ComboBox)
this.form.addControl('data', new AutoCompleteFieldDataEditorModel().buildForm()); //TODO SEE
this.form.get('data').setValidators(this.myCustomValidators._atLeastOneElementListValidator('autoCompleteSingleDataList'));
this.form.get('data').setValidators(this.myCustomValidators.atLeastOneElementListValidator('autoCompleteSingleDataList'));
this.form.get('data').updateValueAndValidity();
@ -450,7 +450,7 @@ export class DatasetProfileEditorFieldComponent extends BaseComponent implements
this.form.get('viewStyle').get('renderStyle').setValue(DatasetProfileFieldViewStyle.RadioBox)
this.form.addControl('data', new RadioBoxFieldDataEditorModel().buildForm());
this.form.get('data').setValidators(this.myCustomValidators._atLeastOneElementListValidator('options'));
this.form.get('data').setValidators(this.myCustomValidators.atLeastOneElementListValidator('options'));
this.form.get('data').updateValueAndValidity();
break;
@ -504,6 +504,10 @@ export class DatasetProfileEditorFieldComponent extends BaseComponent implements
this.form.addControl('data', new ValidationDataEditorModel().buildForm());
break;
}
this.form.get('data').updateValueAndValidity();
this.form.get('viewStyle').get('renderStyle').updateValueAndValidity();
this.form.updateValueAndValidity();
setTimeout(() => { //TODO
this.showPreview = true;
});

View File

@ -130,6 +130,8 @@ export class DatasetProfileEditorSectionFieldSetComponent extends BaseComponent
if(el){
el.scrollIntoView({behavior: "smooth"});
}
}else{//scroll on top
window.scrollTo({top:0, behavior:'smooth'});
}
}

View File

@ -13,7 +13,8 @@
<div class="hint col-12">{{'DATASET-PROFILE-EDITOR.STEPS.SECTION-INFO.SECTION-NAME-HINT' | translate}}</div>
<mat-form-field class="col-12">
<input matInput type="text" placeholder="{{'DATASET-PROFILE-EDITOR.STEPS.FORM.SECTION.FIELDS.TITLE' | translate}}"
formControlName="title">
formControlName="title" required>
<mat-error >{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field>
<!-- <mat-form-field class="col-md-3">
<mat-select placeholder="{{'DATASET-PROFILE-EDITOR.STEPS.FORM.SECTION.FIELDS.PAGE' | translate}}" formControlName="page"
@ -30,7 +31,8 @@
<div class="hint col-12">{{'DATASET-PROFILE-EDITOR.STEPS.SECTION-INFO.SECTION-DESCRIPTION-HINT' | translate}}</div>
<mat-form-field class="col-12">
<input matInput type="text" placeholder="{{'DATASET-PROFILE-EDITOR.STEPS.FORM.SECTION.FIELDS.DESCRIPTION' | translate}}"
formControlName="description">
formControlName="description" required>
<mat-error >{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field>

View File

@ -4,16 +4,64 @@ import { AbstractControl, FormArray, ValidationErrors, ValidatorFn } from "@angu
export class EditorCustomValidators{
public _atLeastOneElementListValidator(arrayToCheck): ValidatorFn{
public atLeastOneElementListValidator(arrayToCheck): ValidatorFn{
return (control: AbstractControl): ValidationErrors | null=>{
const fa = control.get(arrayToCheck) as FormArray;
if(fa.length === 0){
return {emptyArray: true};
return {[EditorCustomValidatorsEnum.emptyArray]: true};
}
return null;
}
}
public pagesHaveAtLeastOneSection(pagesArrayName:string,sectionsArrayName:string ): ValidatorFn{
return (control: AbstractControl): ValidationErrors | null=>{
const pages = control.get(pagesArrayName) as FormArray;
const sections = control.get(sectionsArrayName) as FormArray;
const pageIdsArray = pages.controls.map(page=>page.get('id').value);
const sectionsPageIds = sections.controls.map(section=> section.get('page').value);
let invalidMessage = null;
pageIdsArray.forEach(pageId=>{
if(!sectionsPageIds.includes(pageId)){
invalidMessage = {[EditorCustomValidatorsEnum.atLeastOneSectionInPage]:true};
}
})
return invalidMessage;
}
}
public sectionHasAtLeastOneChildOf(fieldsetsArrayName, sectionsArrayName): ValidatorFn{
return (control: AbstractControl): ValidationErrors | null=>{
const fieldsets = control.get(fieldsetsArrayName) as FormArray;
const sections = control.get(sectionsArrayName) as FormArray;
if((fieldsets && fieldsets.length) || (sections && sections.length)){
return null;
}
return {[EditorCustomValidatorsEnum.sectionMustHaveOneChild] : true};
}
}
}
export enum EditorCustomValidatorsEnum{
sectionMustHaveOneChild = "sectionMustHaveOneChild",
atLeastOneSectionInPage = 'atLeastOneSectionInPage',
emptyArray="emptyArray"
}

View File

@ -40,7 +40,7 @@
<span (click)="stepper.selectedIndex=idx"
class="stepper-title-label"
[ngClass]="{'stepper-title-label-locked': !isStepUnlocked(idx),'stepper-title-label-completed':idx < stepper.selectedIndex} ">
<ng-container *ngIf="isStepCompleted(idx) else numberLabel">
<ng-container *ngIf="(step.completed &&(idx!=steps.length-1)) else numberLabel">
<mat-icon style="font-size:0.7em; height: 0px;">done</mat-icon>
</ng-container>
<ng-template #numberLabel>
@ -119,7 +119,8 @@
<mat-horizontal-stepper [linear]="true" #stepper class="stepper" (selectionChange)="onMatStepperSelectionChange($event)" style="padding-left: 8px; padding-right: 15px;">
<mat-step [label]="'DATASET-PROFILE-EDITOR.STEPS.GENERAL-INFO.TITLE' | translate" [stepControl]="basicInfo">
<!-- IMPORTANT TO BE !INVALID (WHEN THE TEMPLATE IS FINALIZED THE CONTORLS ARE DISABLED) -->
<mat-step [label]="'DATASET-PROFILE-EDITOR.STEPS.GENERAL-INFO.TITLE' | translate" [completed]="(!form.get('label').invalid && !form.get('description').invalid && !form.get('language').invalid)" >
<!-- <ng-template matStepLabel>{{'DATASET-PROFILE-EDITOR.STEPS.GENERAL-INFO.TITLE' | translate}}
</ng-template> -->
<div class="row">
@ -127,7 +128,7 @@
<div class="heading">1.1 {{'DATASET-PROFILE-EDITOR.STEPS.GENERAL-INFO.DATASET-TEMPLATE-NAME'| translate}} *</div>
<div class="hint">{{'DATASET-PROFILE-EDITOR.STEPS.GENERAL-INFO.DATASET-TEMPLATE-NAME-HINT'| translate}}</div>
<mat-form-field class="full-width basic-info-input">
<input matInput formControlName="label"
<input matInput [formControl]="form.get('label')"
placeholder="{{'DATASET-PROFILE-EDITOR.FIELDS.DATASET-TITLE' | translate}}" required>
<mat-error *ngIf="form.get('label').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' |
translate}}
@ -139,7 +140,7 @@
<!-- <div class="hint">{{'DMP-EDITOR.MAIN-INFO.HINT' | translate}}</div> -->
<div class="hint">{{'DATASET-PROFILE-EDITOR.STEPS.GENERAL-INFO.DATASET-TEMPLATE-DESCRIPTION-HINT'| translate}}</div>
<mat-form-field class="full-width basic-info-input">
<textarea matInput formControlName="description" cdkTextareaAutosize cdkAutosizeMinRows="4" cdkAutosizeMaxRows="5"
<textarea matInput [formControl]="form.get('description')" cdkTextareaAutosize cdkAutosizeMinRows="4" cdkAutosizeMaxRows="5"
placeholder="{{'DATASET-PROFILE-EDITOR.STEPS.GENERAL-INFO.DATASET-TEMPLATE-DESCRIPTION-PLACEHOLDER'| translate}}" required>
</textarea>
<mat-error *ngIf="form.get('description').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED'
@ -153,7 +154,7 @@
<div class="heading">1.3 {{'DATASET-PROFILE-EDITOR.STEPS.GENERAL-INFO.DATASET-TEMPLATE-LANGUAGE'| translate}} *</div>
<mat-form-field class="full-width basic-info-input">
<!-- <input matInput formControlName="description" placeholder="{{'DATASET-PROFILE-EDITOR.FIELDS.DATASET-DESCRIPTION' | translate}}" required> -->
<mat-select formControlName="language" placeholder="{{'DATASET-PROFILE-EDITOR.STEPS.GENERAL-INFO.DATASET-TEMPLATE-SELECT-LANGUAGE'| translate}}">
<mat-select [formControl]="form.get('language')" placeholder="{{'DATASET-PROFILE-EDITOR.STEPS.GENERAL-INFO.DATASET-TEMPLATE-SELECT-LANGUAGE'| translate}}">
<mat-option *ngFor="let lang of getLanguageInfos()" [value]="lang.code">
{{ lang.name }}
</mat-option>
@ -181,7 +182,7 @@
</div>
</div>
</mat-step> -->
<mat-step [label]="'DATASET-PROFILE-EDITOR.STEPS.FORM.TITLE' | translate" [stepControl]="form">
<mat-step [label]="'DATASET-PROFILE-EDITOR.STEPS.FORM.TITLE' | translate" [completed]="form.valid">
<!-- <ng-template matStepLabel>{{'DATASET-PROFILE-EDITOR.STEPS.FORM.TITLE' | translate}}</ng-template> -->
<div class="row">
@ -200,7 +201,8 @@
(removeEntry)="onRemoveEntry($event)"
[itemSelected]="selectedTocEntry"
[viewOnly]="viewOnly"
(dataNeedsRefresh)="onDataNeedsRefresh()">
(dataNeedsRefresh)="onDataNeedsRefresh()"
[colorizeInvalid]="colorizeInvalid">
</dataset-profile-table-of-contents>
</div>
</div>
@ -224,6 +226,7 @@
<div class="hint">{{'DATASET-PROFILE-EDITOR.STEPS.PAGE-INFO.PAGE-NAME-HINT' | translate}}</div>
<mat-form-field>
<input type="text" matInput formControlName="title">
<mat-error >{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field>
</div>
@ -402,7 +405,7 @@
<!-- SAVE BUTTON -->
<div class="col-6 d-flex" *ngIf="!viewOnly">
<div class="row mt-4">
<ng-container *ngIf="false">
<ng-container *ngIf="true">
<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>
@ -440,12 +443,20 @@
</div>
</ng-container>
<!--
<div class="row">
<!-- <div class="row">
<button (click)="printForm()">
console form
</button>
</div> -->
<!--
<div *ngIf="form">{{form.value | json}}</div>
<br>
<br>
<div class="row">
{{form.controls?.sections.value |json}}
</div> -->
<!-- {{form.value | json}} -->
<!-- <div class="row">

View File

@ -41,7 +41,7 @@ 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';
import { SideNavService } from '@app/core/services/sidenav/side-nav.sevice';
import { EditorCustomValidators } from './custom-validators/editor-custom-validators';
import { EditorCustomValidators, EditorCustomValidatorsEnum } from './custom-validators/editor-custom-validators';
import { CustomErrorValidator } from '@common/forms/validation/custom-validator';
import { STEPPER_ANIMATIONS } from './animations/animations';
const skipDisable: any[] = require('../../../../../assets/resources/skipDisable.json');
@ -72,6 +72,8 @@ export class DatasetProfileEditorComponent extends BaseComponent implements OnIn
errorMessages: string[] = [];
tocEntryEnumValues = ToCEntryType;
colorizeInvalid:boolean = false;
customEditorValidators = new EditorCustomValidators();
@ -187,7 +189,7 @@ export class DatasetProfileEditorComponent extends BaseComponent implements OnIn
} else {
this.dataModel = new DatasetProfileEditorModel();
this.form = this.dataModel.buildForm();
this.form.setValidators([this.customEditorValidators._atLeastOneElementListValidator('pages'), this.customEditorValidators._atLeastOneElementListValidator('sections')])
this.form.setValidators([this.customEditorValidators.atLeastOneElementListValidator('pages'), this.customEditorValidators.pagesHaveAtLeastOneSection('pages', 'sections')])
if (this.dataModel.status === DatasetProfileEnum.FINALIZED) {
this.form.disable();
this.viewOnly = true;
@ -207,10 +209,11 @@ export class DatasetProfileEditorComponent extends BaseComponent implements OnIn
prepareForm() {
this.visibilityRulesService.buildVisibilityRules([],this.form);
this.form.setValidators([this.customEditorValidators._atLeastOneElementListValidator('pages'), this.customEditorValidators._atLeastOneElementListValidator('pages')]);
this.form.setValidators([this.customEditorValidators.atLeastOneElementListValidator('pages'),this.customEditorValidators.pagesHaveAtLeastOneSection('pages', 'sections')]);
this.form.valueChanges
.pipe(takeUntil(this._destroyed))
.subscribe(change => {
// this.datasetProfileService.preview(this.form.value)
// .pipe(takeUntil(this._destroyed))
// .subscribe(dataset => {
@ -230,8 +233,6 @@ export class DatasetProfileEditorComponent extends BaseComponent implements OnIn
this.selectedTocEntry = tocentries[0];
}
//this.getPreview();
}
onIsMultiplicityEnabledChange(isMultiplicityEnabled: boolean) {
@ -241,11 +242,11 @@ export class DatasetProfileEditorComponent extends BaseComponent implements OnIn
}
}
addSection() {
const section: SectionEditorModel = new SectionEditorModel();
this.dataModel.sections.push(section);
(<FormArray>this.form.get('sections')).push(section.buildForm());
}
// addSection() {
// const section: SectionEditorModel = new SectionEditorModel();
// this.dataModel.sections.push(section);
// (<FormArray>this.form.get('sections')).push(section.buildForm());
// }
addPage() {
const page: PageEditorModel = new PageEditorModel(this.dataModel.pages.length);
@ -376,6 +377,11 @@ export class DatasetProfileEditorComponent extends BaseComponent implements OnIn
});
}
updateValidity(){
// this.form.updateValueAndValidity();
console.log('updagted');
}
getFilenameFromContentDispositionHeader(header: string): string {
const regex: RegExp = new RegExp(/filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/g);
@ -399,15 +405,19 @@ export class DatasetProfileEditorComponent extends BaseComponent implements OnIn
}
checkFormValidation() {
if (!this.form.valid) {
this.nestedIndex = -1;
this.form.markAllAsTouched();
this.printErrors(this.form);
this.showValidationErrorsDialog();
this.nestedCount = [];
this.nestedIndex = 0;
this.errorMessages = [];
}
this.colorizeInvalid = true;
this.printMyErrors(this.form);
// if (!this.form.valid) {
// this.nestedIndex = -1;
// this.form.markAllAsTouched();
// this.printErrors(this.form);
// this.showValidationErrorsDialog();
// this.nestedCount = [];
// this.nestedIndex = 0;
// this.errorMessages = [];
// }
}
printErrors(rootform: FormGroup) {
@ -740,7 +750,7 @@ export class DatasetProfileEditorComponent extends BaseComponent implements OnIn
// this.dataModel.pages.push(page);
pagesArray.push(pageForm);
// this.form.updateValueAndValidity();
this.refreshToCEntries();
this.selectedTocEntry = this._findTocEntryById(pageForm.get('id').value, this.toCEntries);
@ -757,6 +767,7 @@ export class DatasetProfileEditorComponent extends BaseComponent implements OnIn
section.page = parent.id;
section.ordinal = sectionsArray.length;
sectionsArray.push(section.buildForm());
// this.form.updateValueAndValidity();
} else if( parent.type == ToCEntryType.Section) { //SUBSECTION OF SECTION
sectionsArray = parent.form.get('sections') as FormArray;
@ -774,6 +785,8 @@ export class DatasetProfileEditorComponent extends BaseComponent implements OnIn
const sectionAdded = sectionsArray.at(sectionsArray.length -1) as FormGroup;
sectionAdded.setValidators(this.customEditorValidators.sectionHasAtLeastOneChildOf('fieldSets','sections'));
sectionAdded.updateValueAndValidity();
this.refreshToCEntries();
@ -817,7 +830,7 @@ export class DatasetProfileEditorComponent extends BaseComponent implements OnIn
break;
}
// this.refreshToCEntries();
this.form.updateValueAndValidity();
}
@ -912,27 +925,29 @@ export class DatasetProfileEditorComponent extends BaseComponent implements OnIn
pages.removeAt(pageIndex);
//clean up sections of removed page
const sections = (this.form.get('sections') as FormArray).controls;
const sections = (this.form.get('sections') as FormArray);
const updatedSections = new FormArray([]);
const sectionsIndexToBeRemoved = [];
for (let i = 0; i < sections.length; i++) {
let section = sections[i];
if (section.get('page').value != tce.id) {
updatedSections.push(section);
sections.controls.forEach((section,idx)=>{
if(section.get('page').value === tce.id){
sectionsIndexToBeRemoved.push(idx);
}
}
//replace sections value
this.form.controls.sections = updatedSections;
});
//update page orders
if(sectionsIndexToBeRemoved.length){
sectionsIndexToBeRemoved.reverse().forEach(index=>{
sections.removeAt(index);
});
}
//update page ordinals
for(let i=0; i<pages.length; i++){
pages.at(i).get('ordinal').patchValue(i);
}
//update validity
this.form.controls.sections.updateValueAndValidity();
// this.form.controls.sections.updateValueAndValidity();
}
break;
@ -1028,6 +1043,7 @@ export class DatasetProfileEditorComponent extends BaseComponent implements OnIn
//in case selectedtocentrhy is child of the removed element
this.refreshToCEntries();
this.form.updateValueAndValidity();
}
@ -1339,22 +1355,22 @@ export class DatasetProfileEditorComponent extends BaseComponent implements OnIn
get basicInfo(){
// get basicInfo(){
const label = this.form.get('label');
const description = this.form.get('description');
const language = this.form.get('language');
// const label = this.form.get('label');
// const description = this.form.get('description');
// const language = this.form.get('language');
const fg = new FormGroup({
label: label,
description: description,
language: language
})
// const fg = new FormGroup({
// label: label,
// description: description,
// language: language
// })
return fg;
}
// return fg;
// }
@ -1421,14 +1437,12 @@ export class DatasetProfileEditorComponent extends BaseComponent implements OnIn
}
isStepCompleted(stepIndex: number){
isStepCompleted(stepIndex: number){ //TODO NA TO DOUME MIPOS DEN XREIAZETAI
let stepCompleted = false;
this.steps.forEach((step,index)=>{
if(stepIndex === index){
if(step.stepControl){
stepCompleted = step.stepControl.valid;
}
stepCompleted = step.completed;
}
});
@ -1437,13 +1451,17 @@ export class DatasetProfileEditorComponent extends BaseComponent implements OnIn
isStepUnlocked(stepIndex: number): boolean{
if(stepIndex === 0) return true;
if(stepIndex <0 ) return false;
//if previous step is valid then unlock
let stepUnlocked: boolean = false;
if(!this.isStepUnlocked(stepIndex -1)) return false;
this.steps.forEach((step,index)=>{
if(index+1 == stepIndex){//previous step
if(step.stepControl.valid){
if(step.completed){
stepUnlocked = true;
}
}
@ -1453,27 +1471,27 @@ export class DatasetProfileEditorComponent extends BaseComponent implements OnIn
}
validateStep(step: CdkStep){
if(step.hasError){
this.printMyErrors(this.form);
}
// if(step.hasError){
// this.printMyErrors(this.form);
// }
}
getFormValidationErrors() {
Object.keys(this.form.controls).forEach(key => {
// getFormValidationErrors() {
// Object.keys(this.form.controls).forEach(key => {
const controlErrors: ValidationErrors = this.form.get(key).errors;
if (controlErrors != null) {
Object.keys(controlErrors).forEach(keyError => {
console.log('Key control: ' + key + ', keyError: ' + keyError + ', err value: ', controlErrors[keyError]);
});
}
});
// const controlErrors: ValidationErrors = this.form.get(key).errors;
// if (controlErrors != null) {
// Object.keys(controlErrors).forEach(keyError => {
// console.log('Key control: ' + key + ', keyError: ' + keyError + ', err value: ', controlErrors[keyError]);
// });
// }
// });
if(this.form.invalid){
console.log('this form is invalid!');
console.log(this.form.errors);
}
}
// if(this.form.invalid){
// console.log('this form is invalid!');
// console.log(this.form.errors);
// }
// }
@ -1481,11 +1499,26 @@ export class DatasetProfileEditorComponent extends BaseComponent implements OnIn
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]));
switch(keyError){
case EditorCustomValidatorsEnum.atLeastOneSectionInPage:
errmess.push('Each section must have at least one subsection');
break;
case EditorCustomValidatorsEnum.emptyArray:
errmess.push(numbering+" needs more information.")
break;
case EditorCustomValidatorsEnum.sectionMustHaveOneChild:
errmess.push(numbering+" must have either subsection or input sets.")
break;
default:
if (typeof errors[keyError] === 'boolean') {
errmess.push(numbering + ' ' + key + ' is ' + keyError);
} else {
errmess.push(numbering + ' ' + key + ': ' + keyError + ': ' + JSON.stringify(errors[keyError]));
}
}
});
return errmess;
@ -1495,59 +1528,67 @@ export class DatasetProfileEditorComponent extends BaseComponent implements OnIn
printMyErrors(form: AbstractControl){
// this._printToCentriesErrrors(this.toCEntries);
const result = this._getErrors(form);
// console.log('result', result);
// console.log('got errors ');
// console.log(result);
if(result){
form.markAllAsTouched();
if(result && form.invalid){
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));
if(result.length){
form.markAllAsTouched();
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=>{
// console.log(err);
if(err.key){
errmess.push(`Missing ${err.key}` );
}else{
indexes.reverse().forEach(index=>{
result.splice(index,1);
});
errmess.push('Make sure you provide a section and a subsection');
}
// errmess.push(...this._buildErrorMessage(err.errors,"", err.key) );
result.forEach(err=>{
errmess.push(`Missing ${err.key}` );
})
})
}
// console.log(result);
@ -1627,11 +1668,7 @@ export class DatasetProfileEditorComponent extends BaseComponent implements OnIn
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';
@ -1650,13 +1687,10 @@ export class DatasetProfileEditorComponent extends BaseComponent implements OnIn
const keys = Object.keys(controls);
keys.forEach(key=>{
const control = controls[key];
if(control.valid) return;
if(!control.invalid) return; //// !!!!! Important to be !invalid. (In case the template is finalized)
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)
invalidControls.push({
errors:ctrl.errors,
@ -1674,26 +1708,38 @@ export class DatasetProfileEditorComponent extends BaseComponent implements OnIn
// invalidSubControls:[]//TODO TO CHECK
// });
// }
if(control.errors){
// invalidControls.push({
// id: aControl.get('id')? aControl.get('id').value: null,
// errors:aControl.errors,
// key: aControl.get('title')? aControl.get('title').value: 'unspecified',
// invalidSubControls:[]//TODO TO CHECK
// });
invalidControls.push({
errors:control.errors,
id: control.get('id')? control.get('id').value: null,
invalidSubControls: [],
key: key
});
}
//THE ONE WE REMOVED
// if(control.errors){
// // invalidControls.push({
// // id: aControl.get('id')? aControl.get('id').value: null,
// // errors:aControl.errors,
// // key: aControl.get('title')? aControl.get('title').value: 'unspecified',
// // invalidSubControls:[]//TODO TO CHECK
// // });
// invalidControls.push({
// errors:control.errors,
// id: control.get('id')? control.get('id').value: null,
// invalidSubControls: [],
// key: key
// });
// }
invalidControls.push(...this._getErrors(control)) ;
}
});
/**In case there is an error in a formgroup then the validator probably is custom */
if(aControl.errors){
invalidControls.push({
errors:aControl.errors,
id: aControl.get('id')? aControl.get('id').value: null,
invalidSubControls: [],
key: aControl.get('title')?aControl.get('title').value: null
});
}
break;
case 'fa':

View File

@ -22,7 +22,7 @@
[ngStyle]="{'font-size' : (parentLink.type == tocEntryType.FieldSet? '.9rem':'1rem')}"
> -->
<span class="table-label-element" [ngClass]="{'table-label-element-active': itemSelected?.id == parentLink?.id}" (click)="itemClicked(parentLink)"
<span class="table-label-element" [ngClass]="{'table-label-element-active': itemSelected?.id == parentLink?.id, 'text-danger': colorError()}" (click)="itemClicked(parentLink)"
[ngStyle]="{'font-size' : (parentLink.type == tocEntryType.FieldSet? '.9rem':'1rem')}"
>
<!-- {{parentLink?.numbering}} {{parentLink?.label? parentLink?.label : 'DATASET-PROFILE-EDITOR.STEPS.GENERAL-INFO.UNTITLED' | translate}} -->
@ -33,10 +33,14 @@
</div>
<div class="col-auto d-flex align-items-center" >
<span class="badge-ball"
*ngIf="!(!((parentLink?.subEntriesType == tocEntryType.FieldSet) && !selectedItemInLinks) || itemSelected?.id == parentLink.id ||isDragging)">
{{parentLink.subEntries?.length}}
</span>
<ng-container *ngIf="!(!((parentLink?.subEntriesType == tocEntryType.FieldSet) && !selectedItemInLinks) || itemSelected?.id == parentLink.id ||isDragging)">
<mat-icon class="text-danger" style="font-size: 1.4em;" *ngIf="!colorError() && parentLink?.form.invalid && colorizeInvalid && allFieldsAreTouched(parentLink?.form)">priority_high</mat-icon>
<span class="badge-ball"
>
{{parentLink.subEntries?.length}}
</span>
</ng-container>
<span style="cursor: pointer;" (click)="deleteEntry(parentLink)"
@ -109,7 +113,8 @@
[overContainerId]="overContainerId"
[isDragging]="isDragging"
[draggingItemId]="draggingItemId"
[parentRootId]="parentRootId">
[parentRootId]="parentRootId"
[colorizeInvalid]="colorizeInvalid">
</app-dataset-profile-table-of-contents-internal-section>

View File

@ -1,7 +1,7 @@
import { CdkDrag, CdkDragDrop, CdkDropList, moveItemInArray } from '@angular/cdk/drag-drop';
import { DOCUMENT } from '@angular/common';
import { Component, EventEmitter, Inject, Input, OnInit, Output, SimpleChanges, ViewChild } from '@angular/core';
import { FormArray } from '@angular/forms';
import { AbstractControl, FormArray, FormControl, FormGroup } from '@angular/forms';
import { BaseComponent } from '@common/base/base.component';
import { Foo, ToCEntry, ToCEntryType } from '../table-of-contents-entry';
@ -31,6 +31,8 @@ export class DatasetProfileTableOfContentsInternalSection extends BaseComponent
@Input() draggingItemId: string;
@Input() parentRootId: string;
@Input() colorizeInvalid:boolean = false;
@Input() viewOnly: boolean;
// @Input() dropListGroup: Set<string> = new Set<string>();
@Input() dropListGroup: string[];
@ -229,4 +231,78 @@ export class DatasetProfileTableOfContentsInternalSection extends BaseComponent
return tocEntryFound? tocEntryFound: null;
}
colorError():boolean{
if(!this.colorizeInvalid) return false;
const form = this.parentLink.form;
if(!form || form.valid || !form.touched) return false;
const allFieldsAreTouched = this.allFieldsAreTouched(form);
//fieldset may have errros that are inside its controls and not in the fieldsetFormGroup
if(this.parentLink.type === this.tocEntryType.FieldSet && allFieldsAreTouched) return true;
if(form.errors && allFieldsAreTouched) return true;
//checking form controls if have errors
let hasErrors = false;
if(allFieldsAreTouched){
if(form instanceof FormGroup){
const formGroup = form as FormGroup;
const controls = Object.keys(formGroup.controls);
controls.forEach(control=>{
if(formGroup.get(control).errors){
hasErrors = true;
}
})
}
}
return hasErrors;
}
allFieldsAreTouched(aControl:AbstractControl){//auto na testaroume
if(!aControl|| aControl.untouched) return false;
if(aControl instanceof FormControl){
return aControl.touched;
}else if(aControl instanceof FormGroup){
const controlKeys = Object.keys((aControl as FormGroup).controls);
let areAllTouched = true;
controlKeys.forEach(key=>{
if(!this.allFieldsAreTouched(aControl.get(key))){
areAllTouched = false;
}
})
// const areAllTouched = controlKeys.reduce((acc, key)=>acc && this._allFieldsAreTouched(aControl.get(key)), true);
return areAllTouched;
}else if(aControl instanceof FormArray){
const controls = (aControl as FormArray).controls;
// const areAllTouched = controls.reduce((acc, control)=>acc && this._allFieldsAreTouched(control), true);
let areAllTouched = true;
// controls.reduce((acc, control)=>acc && this._allFieldsAreTouched(control), true);
controls.forEach(control=>{
if(!this.allFieldsAreTouched(control)){
areAllTouched = false;
}
});
return areAllTouched;
}
return false;
}
}

View File

@ -22,6 +22,7 @@
[draggingItemId]="draggingItemId"
[parentRootId]="ROOT_ID"
style="padding-right: 1em;"
[colorizeInvalid]="colorizeInvalid"
></app-dataset-profile-table-of-contents-internal-section>
<!-- <span *ngFor="let link of links; let i = index" (click)="toggle(link); goToStep(link);" class="docs-link mt-0">

View File

@ -50,6 +50,7 @@ export class DatasetProfileTableOfContents extends BaseComponent implements OnIn
@Output() dataNeedsRefresh = new EventEmitter<void>();
@Input() itemSelected: ToCEntry;
@Input() colorizeInvalid: boolean = false;
// @Input() set itemSelected(entry:ToCEntry){
// this._itemSelected = entry;

View File

@ -76,11 +76,11 @@
}
}
::ng-deep .mat-form-field-wrapper {
:host ::ng-deep .mat-form-field-wrapper {
background-color: white !important;
padding-bottom: 0 !important;
}
::ng-deep .mat-form-field-appearance-outline .mat-form-field-infix {
:host ::ng-deep .mat-form-field-appearance-outline .mat-form-field-infix {
padding: 0.3rem 0rem 0.6rem 0rem !important;
}

View File

@ -10,11 +10,11 @@
padding: 0.4rem;
}
::ng-deep .mat-form-field-wrapper {
:host ::ng-deep .mat-form-field-wrapper {
background-color: white !important;
padding-bottom: 0 !important;
}
::ng-deep .mat-form-field-appearance-outline .mat-form-field-infix {
:host ::ng-deep .mat-form-field-appearance-outline .mat-form-field-infix {
padding: 0.3rem 0rem 0.6rem 0rem !important;
}

View File

@ -122,7 +122,7 @@
background-color: #2e74b649;
}
::ng-deep .mat-form-field-wrapper {
:host ::ng-deep .mat-form-field-wrapper {
background-color: white !important;
padding-bottom: 0 !important;
}