import { of as observableOf, Observable } from 'rxjs'; import { HttpClient, HttpErrorResponse } from '@angular/common/http'; import { AfterViewInit, Component, OnChanges, OnInit, QueryList, SimpleChanges, ViewChild } from '@angular/core'; import { AbstractControl, Form, FormArray, FormBuilder, FormControl, FormGroup, ValidationErrors, ValidatorFn, Validators } from '@angular/forms'; import { MatDialog } from '@angular/material/dialog'; import { MatHorizontalStepper, MatStep } from '@angular/material/stepper'; import { ActivatedRoute, ParamMap, Router } from '@angular/router'; import { TranslateService } from '@ngx-translate/core'; import { map, takeUntil } from 'rxjs/operators'; import * as FileSaver from 'file-saver'; import { BaseComponent } from '@common/base/base.component'; import { DatasetProfileEditorModel } from '@app/ui/admin/dataset-profile/editor/dataset-profile-editor-model'; import { DatasetWizardModel } from '@app/core/model/dataset/dataset-wizard'; import { BreadcrumbItem } from '@app/ui/misc/breadcrumb/definition/breadcrumb-item'; import { DatasetProfileService } from '@app/core/services/dataset-profile/dataset-profile.service'; import { LoggingService } from '@app/core/services/logging/logging-service'; import { UiNotificationService, SnackBarNotificationLevel } from '@app/core/services/notification/ui-notification-service'; import { DatasetProfile } from '@app/core/model/admin/dataset-profile/dataset-profile'; import { DatasetProfileEnum } from '@app/core/common/enum/dataset-profile'; import { SectionEditorModel } from '@app/ui/admin/dataset-profile/admin/section-editor-model'; import { PageEditorModel } from '@app/ui/admin/dataset-profile/admin/page-editor-model'; import { DatasetStatus } from '@app/core/common/enum/dataset-status'; import { ConfirmationDialogComponent } from '@common/modules/confirmation-dialog/confirmation-dialog.component'; import { LanguageInfo } from '@app/core/model/language-info'; import { LanguageInfoService } from '@app/core/services/culture/language-info-service'; import { FormValidationErrorsDialogComponent } from '@common/forms/form-validation-errors-dialog/form-validation-errors-dialog.component'; import { MatomoService } from '@app/core/services/matomo/matomo-service'; import { Link, LinkToScroll } from '@app/ui/misc/dataset-description-form/tableOfContentsMaterial/table-of-contents'; import { DatasetWizardService } from '@app/core/services/dataset-wizard/dataset-wizard.service'; import { DatasetWizardEditorModel } from '@app/ui/dataset/dataset-wizard/dataset-wizard-editor.model'; import { NewEntryType, ToCEntry, ToCEntryType } from '../table-of-contents/table-of-contents-entry'; import { EnumUtils } from '@app/core/services/utilities/enum-utils.service'; import { FieldSetEditorModel } from '../admin/field-set-editor-model'; import { Guid } from '@common/types/guid'; import { FieldEditorModel } from '../admin/field-editor-model'; import { VisibilityRulesService } from '@app/ui/misc/dataset-description-form/visibility-rules/visibility-rules.service'; 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'; import { SideNavService } from '@app/core/services/sidenav/side-nav.sevice'; import { EditorCustomValidators, EditorCustomValidatorsEnum } from './custom-validators/editor-custom-validators'; import { CustomErrorValidator } from '@common/forms/validation/custom-validator'; import { STEPPER_ANIMATIONS } from './animations/animations'; import { DatasetProfileComboBoxType } from '@app/core/common/enum/dataset-profile-combo-box-type'; const skipDisable: any[] = require('../../../../../assets/resources/skipDisable.json'); @Component({ selector: 'app-dataset-profile-editor-component', templateUrl: './dataset-profile-editor.component.html', styleUrls: ['./dataset-profile-editor.component.scss'], animations:[...STEPPER_ANIMATIONS], providers:[VisibilityRulesService] }) export class DatasetProfileEditorComponent extends BaseComponent implements OnInit { isNew = true; isNewVersion = false; isClone = false; isDeleted = false; dataModel: DatasetProfileEditorModel; form: FormGroup; previewerFormGroup: FormGroup; private datasetProfileId: string; newVersionId: string; dataWizardModel: DatasetWizardModel; breadCrumbs: Observable; @ViewChild('stepper', { static: false }) stepper: MatHorizontalStepper; viewOnly = false; nestedCount: number[] = []; nestedIndex: number = 0; errorMessages: string[] = []; tocEntryEnumValues = ToCEntryType; colorizeInvalid:boolean = false; // customEditorValidators = new EditorCustomValidators(); // sectionIdPreviewed:string = null; // currentSubForm:FormGroup = null; // currentSectionIndex: number = null; // currentSectionEditorModel: SectionEditorModel = null; constructor( private datasetProfileService: DatasetProfileService, private route: ActivatedRoute, private router: Router, private logger: LoggingService, private uiNotificationService: UiNotificationService, private language: TranslateService, private dialog: MatDialog, private languageInfoService: LanguageInfoService, private httpClient: HttpClient, private matomoService: MatomoService, private enumUtils: EnumUtils, private datasetWizardService: DatasetWizardService, private visibilityRulesService: VisibilityRulesService, private fb: FormBuilder, private sidenavService: SideNavService ) { super(); // this.profileID = route.snapshot.params['id']; // this.cloneId = route.snapshot.params['cloneid']; } ngOnDestroy(){ this.sidenavService.setStatus(true); } ngOnInit() { this.sidenavService.setStatus(false); this.matomoService.trackPageView('Admin: Dataset Profile Edit'); this.route.paramMap.pipe(takeUntil(this._destroyed)).subscribe((paramMap: ParamMap) => { this.datasetProfileId = paramMap.get('id'); const cloneId = paramMap.get('cloneid'); this.newVersionId = paramMap.get('newversionid'); if (this.datasetProfileId != null) { this.isNew = false; this.datasetProfileService.getDatasetProfileById(this.datasetProfileId) .pipe(map(data => data as DatasetProfile), takeUntil(this._destroyed)) .subscribe( data => { try { this.dataModel = new DatasetProfileEditorModel().fromModel(data); // this.isDeleted = this.masterItem.isActive === IsActive.Inactive; if (this.dataModel.status === DatasetProfileEnum.FINALIZED) { this.form = this.dataModel.buildForm(true, skipDisable); this.viewOnly = true; } else { this.form = this.dataModel.buildForm(); } this.prepareForm(); } catch (error) { this.logger.error('Could not parse MasterItem: ' + data); console.log(error) this.uiNotificationService.snackBarNotification(this.language.instant('NOTIFICATIONS.DEFAULT.ERROR'), SnackBarNotificationLevel.Error); } }, error => this.onCallbackError(error) ); this.breadCrumbs = observableOf([{ parentComponentName: 'DatasetProfileListingComponent', label: this.language.instant('NAV-BAR.TEMPLATE'), url: '/dataset-profiles/' + this.datasetProfileId }]); } else if (cloneId != null) { this.isClone = true; this.datasetProfileService.clone(cloneId) .pipe(map(data => data as DatasetProfile), takeUntil(this._destroyed)) .subscribe( data => { try { this.dataModel = new DatasetProfileEditorModel().fromModel(data); // this.isDeleted = this.masterItem.isActive === IsActive.Inactive; this.dataModel.status = DatasetProfileEnum.SAVED; this.form = this.dataModel.buildForm(); this.prepareForm(); } catch { this.logger.error('Could not parse MasterItem: ' + data); this.uiNotificationService.snackBarNotification(this.language.instant('NOTIFICATIONS.DEFAULT.ERROR'), SnackBarNotificationLevel.Error); } }, error => this.onCallbackError(error) ); } else if (this.newVersionId != null) { this.isNewVersion = true; this.datasetProfileService.getDatasetProfileById(this.newVersionId) .pipe(map(data => data as DatasetProfile), takeUntil(this._destroyed)) .subscribe( data => { try { this.dataModel = new DatasetProfileEditorModel().fromModel(data); // this.isDeleted = this.masterItem.isActive === IsActive.Inactive; this.form = this.dataModel.buildForm(); this.form.get('version').setValue(this.form.get('version').value + 1); this.form.controls['label'].disable(); this.form.controls['description'].disable(); this.form.controls['language'].disable(); this.prepareForm(); } catch { this.logger.error('Could not parse MasterItem: ' + data); this.uiNotificationService.snackBarNotification(this.language.instant('NOTIFICATIONS.DEFAULT.ERROR'), SnackBarNotificationLevel.Error); } }, error => this.onCallbackError(error) ); } else { this.dataModel = new DatasetProfileEditorModel(); this.form = this.dataModel.buildForm(); // this.form.setValidators([EditorCustomValidators.atLeastOneElementListValidator('pages'), EditorCustomValidators.pagesHaveAtLeastOneSection('pages', 'sections')]); if (this.dataModel.status === DatasetProfileEnum.FINALIZED) { this.form.disable(); this.viewOnly = true; } // this.addSection(); // this.addPage(); this.visibilityRulesService.buildVisibilityRules([],this.form); setTimeout(() => { this.steps = this.stepper.steps; }); this._initializeToCEntries(); } }); } private _initializeToCEntries(){ const tocentries = this.refreshToCEntries();//tocentries are sorted based on their ordinal value this._updateOrdinals(tocentries); if(tocentries && tocentries.length){ this.selectedTocEntry = tocentries[0]; } // this._initializeFormValidity(tocentries); } prepareForm() { this.visibilityRulesService.buildVisibilityRules([],this.form); // this.form.setValidators([EditorCustomValidators.atLeastOneElementListValidator('pages'),EditorCustomValidators.pagesHaveAtLeastOneSection('pages', 'sections')]); // this.form.updateValueAndValidity(); this.form.valueChanges .pipe(takeUntil(this._destroyed)) .subscribe(change => { // this.datasetProfileService.preview(this.form.value) // .pipe(takeUntil(this._destroyed)) // .subscribe(dataset => { // const datasetModel = new DatasetWizardModel(); // datasetModel.datasetProfileDefinition = JsonSerializer.fromJSONObject(dataset, DatasetProfileDefinitionModel); // this.dataWizardModel = datasetModel; // this.previewerFormGroup = this.dataWizardModel.buildForm().get('datasetProfileDefinition'); // }); }); setTimeout(() => { this.steps = this.stepper.steps; }); this._initializeToCEntries(); } onIsMultiplicityEnabledChange(isMultiplicityEnabled: boolean) { if (!isMultiplicityEnabled) { (this.form.get('multiplicity').get('min')).setValue(0); (this.form.get('multiplicity').get('max')).setValue(0); } } // addSection() { // const section: SectionEditorModel = new SectionEditorModel(); // this.dataModel.sections.push(section); // (this.form.get('sections')).push(section.buildForm()); // } // addPage() { // const page: PageEditorModel = new PageEditorModel(this.dataModel.pages.length); // this.dataModel.pages.push(page); // (this.form.get('pages')).push(page.buildForm()); // } // DeleteSection(index) { // this.dataModel.sections.splice(index, 1); // (this.form.get('sections')).removeAt(index); // } onSubmit() { let data = this.form.value; if (this.datasetProfileId) { this.datasetProfileService.updateForm(this.datasetProfileId, data) .pipe(takeUntil(this._destroyed)) .subscribe(() => { this.router.navigate(['/dataset-profiles']); this.uiNotificationService.snackBarNotification(this.language.instant('DATASET-PROFILE-EDITOR.FEEDBACK-MESSAGES.SAVE-SUCCESS'), SnackBarNotificationLevel.Success); },error=> this.onCallbackError(error)); } else if (this.newVersionId) { data.label = this.form.get('label').value; data.description = this.form.get('description').value; this.datasetProfileService.newVersion(this.newVersionId, data) .pipe(takeUntil(this._destroyed)) .subscribe(() => { this.router.navigate(['/dataset-profiles']); this.uiNotificationService.snackBarNotification(this.language.instant('DATASET-PROFILE-EDITOR.FEEDBACK-MESSAGES.SAVE-SUCCESS'), SnackBarNotificationLevel.Success); }, error => this.onCallbackErrorNewVersion(error) ); } else { if(this.form.get('status').value != DatasetStatus.Finalized){// forn created and finalized instantly this.form.get('status').setValue(DatasetStatus.Draft); } data = this.form.value; this.datasetProfileService.createForm(data) .pipe(takeUntil(this._destroyed)) .subscribe(() => { this.router.navigate(['/dataset-profiles']); this.uiNotificationService.snackBarNotification(this.language.instant('DATASET-PROFILE-EDITOR.FEEDBACK-MESSAGES.SAVE-SUCCESS'), SnackBarNotificationLevel.Success); }, error=> this.onCallbackError(error)); } } finalize() { //const data = this.form.value; this.form.get('status').setValue(DatasetProfileEnum.FINALIZED); this.onSubmit(); } updateFinalized() { this.datasetProfileService.updateForm(this.datasetProfileId, this.form.getRawValue()) .pipe(takeUntil(this._destroyed)) .subscribe(() => { this.router.navigate(['/dataset-profiles']); this.uiNotificationService.snackBarNotification(this.language.instant('DATASET-PROFILE-EDITOR.FEEDBACK-MESSAGES.SAVE-SUCCESS'), SnackBarNotificationLevel.Success) },error=>this.onCallbackError(error)); } showUpdateButton() { return !this.isNew && this.dataModel.status === DatasetProfileEnum.FINALIZED; } // isStepActive(step: number) { // return this.stepper && this.stepper.selectedIndex === step; // } // onCallbackSuccess(): void { // this.uiNotificationService.snackBarNotification(this.isNew ? this.language.instant('GENERAL.SNACK-BAR.SUCCESSFUL-CREATION') : this.language.instant('GENERAL.SNACK-BAR.SUCCESSFUL-UPDATE'), SnackBarNotificationLevel.Success); // this.router.navigate(['/master-items']); // } onCallbackErrorNewVersion(errorResponse: HttpErrorResponse) { this.uiNotificationService.snackBarNotification(errorResponse.error.message, SnackBarNotificationLevel.Error); } onCallbackError(errorResponse: HttpErrorResponse) { // const error: HttpError = this.httpErrorHandlingService.getError(errorResponse); // if (error.statusCode === 400) { // this.masterItem.validationErrorModel.fromJSONObject(errorResponse.error); // this.formService.validateAllFormFields(this.formGroup); // } else { this.uiNotificationService.snackBarNotification(errorResponse.message, SnackBarNotificationLevel.Warning); // } } // DELETE Function public delete(): void { if (this.datasetProfileId && !this.isNew) { const dialogRef = this.dialog.open(ConfirmationDialogComponent, { restoreFocus: false, data: { message: this.language.instant('GENERAL.CONFIRMATION-DIALOG.DELETE-ITEM'), confirmButton: this.language.instant('GENERAL.CONFIRMATION-DIALOG.ACTIONS.CONFIRM'), cancelButton: this.language.instant('GENERAL.CONFIRMATION-DIALOG.ACTIONS.CANCEL'), isDeleteConfirmation: true } }); dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(result => { if (result) { //this.form.get('status').setValue(DatasetProfileEnum.DELETED); this.datasetProfileService.delete(this.datasetProfileId, this.form.value) .pipe(takeUntil(this._destroyed)) .subscribe( complete => { this.uiNotificationService.snackBarNotification(this.language.instant('GENERAL.SNACK-BAR.SUCCESSFUL-DATASET-PROFILE-DELETE'), SnackBarNotificationLevel.Success); this.router.navigate(['/dataset-profiles']); }, error => { this.onCallbackError(error); if (error.error.statusCode == 674) { this.uiNotificationService.snackBarNotification(this.language.instant('GENERAL.SNACK-BAR.UNSUCCESSFUL-DATASET-PROFILE-DELETE'), SnackBarNotificationLevel.Error); } else { this.uiNotificationService.snackBarNotification(this.language.instant(error.message), SnackBarNotificationLevel.Error); } } ); } }); } } downloadXML(): void { this.datasetProfileService.downloadXML(this.datasetProfileId) .pipe(takeUntil(this._destroyed)) .subscribe(response => { const blob = new Blob([response.body], { type: 'application/xml' }); const filename = this.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); FileSaver.saveAs(blob, filename); }); } getFilenameFromContentDispositionHeader(header: string): string { const regex: RegExp = new RegExp(/filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/g); const matches = header.match(regex); let filename: string; for (let i = 0; i < matches.length; i++) { const match = matches[i]; if (match.includes('filename="')) { filename = match.substring(10, match.length - 1); break; } else if (match.includes('filename=')) { filename = match.substring(9); break; } } return filename; } getLanguageInfos(): LanguageInfo[] { return this.languageInfoService.getLanguageInfoValues(); } checkFormValidation() { 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 = []; // } } //BEFORE REDESIGN // printErrors(rootform: FormGroup) { // if (!rootform.valid) { // Object.keys(rootform.controls).forEach(key => { // const errors = rootform.get(key).errors; // if (errors !== null) { // let numbering: string = ''; // for (let j = 0; j < this.nestedCount.length; j++) { // numbering += this.nestedCount[j]; // if (j < this.nestedIndex) { // numbering += '.'; // } else { // break; // } // } // Object.keys(errors).forEach(keyError => { // if (typeof errors[keyError] === 'boolean') { // this.errorMessages.push(numbering + ' ' + key + ' is ' + keyError); // } else { // this.errorMessages.push(numbering + ' ' + key + ': ' + keyError + ': ' + JSON.stringify(errors[keyError])); // } // }); // } else { // if (rootform.get(key) instanceof FormGroup) { // this.printErrors(rootform.get(key)); // } else if (rootform.get(key) instanceof FormArray) { // this.nestedIndex++; // this.nestedCount[this.nestedIndex] = 0; // for (let childForm of (rootform.get(key)).controls) { // this.nestedCount[this.nestedIndex]++; // this.printErrors(childForm); // } // this.nestedCount[this.nestedIndex] = 0; // this.nestedIndex--; // } // } // }); // } // } // private showValidationErrorsDialog(projectOnly?: boolean) { // const dialogRef = this.dialog.open(FormValidationErrorsDialogComponent, { // disableClose: true, // autoFocus: false, // restoreFocus: false, // data: { // errorMessages: this.errorMessages, // projectOnly: projectOnly // }, // }); // } // links: Link[] = []; // getLinks(currentLinks: Link[]) { // this.links = currentLinks; // } datasetWizardModel: DatasetWizardEditorModel; formGroup: FormGroup; getPreview() { let data = this.form.getRawValue(); this.datasetProfileService.preview(data).subscribe(x => { this.datasetWizardModel = new DatasetWizardEditorModel().fromModel({ datasetProfileDefinition: x }); this.updateVisibilityRules(); this.formGroup = this.datasetWizardModel.buildForm().get('datasetProfileDefinition'); }); //this.formGroupRawValue = JSON.parse(JSON.stringify(this.formGroup.getRawValue())); //this.editMode = this.datasetWizardModel.status === DatasetStatus.Draft; // if (this.datasetWizardModel.status === DatasetStatus.Finalized) { // this.formGroup.disable(); // this.viewOnly = true; // } //if (this.viewOnly) { this.formGroup.disable(); } // For future use, to make Dataset edit like DMP. // this.registerFormListeners(); // this.dmpValueChanged(null); // this.breadCrumbs = observableOf([ // { // parentComponentName: null, // label: this.language.instant('DATASET-LISTING.ACTIONS.CREATE-NEW').toUpperCase(), // url: '/datasets/new/' // }]); // this.datasetWizardService.updateDatasetProfile(this.profileUpdateId) // .pipe(takeUntil(this._destroyed)) // .subscribe(data => { // this.datasetWizardModel = new DatasetWizardEditorModel().fromModel(data); // this.formGroupRawValue = JSON.parse(JSON.stringify(this.formGroup.getRawValue())); // this.needsUpdate(); // this.breadCrumbs = observableOf([ // { // parentComponentName: null, // label: this.language.instant('NAV-BAR.MY-DATASET-DESCRIPTIONS'), // url: '/datasets', // notFoundResolver: [ // // { // // parentComponentName: null, // // label: this.datasetWizardModel.dmp.grant.label, // // url: '/grants/edit/' + this.datasetWizardModel.dmp.grant.id // // }, // { // parentComponentName: null, // label: this.datasetWizardModel.dmp.label, // url: '/plans/edit/' + this.datasetWizardModel.dmp.id, // }, // ] // }]); // this.formGroup = this.datasetWizardModel.buildForm(); // this.editMode = this.datasetWizardModel.status === DatasetStatus.Draft; // if (this.datasetWizardModel.status === DatasetStatus.Finalized) { // this.formGroup.disable(); // this.viewOnly = true; // } // // if (this.viewOnly) { this.formGroup.disable(); } // For future use, to make Dataset edit like DMP. // this.loadDatasetProfiles(); // }); } private refreshToCEntries(): ToCEntry[]{ this.toCEntries = this.getTocEntries(); //update selected tocentry if(this.selectedTocEntry){ this.selectedTocEntry = this._findTocEntryById(this.selectedTocEntry.id, this.toCEntries); } // this.updateOrdinals(this.toCEntries); // this._updateNumbering(this.toCEntries); return this.toCEntries; } /** * Updates entries ordinal form value * based on the index they have on the tocentry array. * Tocentries that are on the same level have distinct ordinal value * * @param tocentries * */ private _updateOrdinals(tocentries: ToCEntry[]){ if(!tocentries || !tocentries.length) return; tocentries.forEach((e,idx)=>{ const ordinalControl = e.form.get('ordinal'); if(ordinalControl){ ordinalControl.setValue(idx); ordinalControl.updateValueAndValidity(); } this._updateOrdinals(e.subEntries); }); } //sort tocentries based on their ordinality private _sortToCentries(entries: ToCEntry[]){ if(!entries || !entries.length) return; entries.sort(this._compareOrdinals); entries.forEach(e=>{ this._sortToCentries(e.subEntries) }); } private _compareOrdinals(a, b){ const aValue = a.form.get('ordinal').value as number; const bValue = b.form.get('ordinal').value as number; // if(!aValue || !bValue) return 0; return aValue - bValue; } private _updateNumbering(entries:ToCEntry[], parentNumbering:string){ if(!entries || !entries.length) return; let prefix =''; if(parentNumbering.length){ prefix = parentNumbering + '.'; } entries.forEach((entry,index)=>{ const numbering = prefix + (index+1); entry.numbering = numbering; this._updateNumbering(entry.subEntries, numbering); }) } toCEntries:ToCEntry[]; getTocEntries(): ToCEntry[] { if (this.form == null) { return []; } const result: ToCEntry[] = []; //build parent pages (this.form.get('pages') as FormArray).controls.forEach((pageElement, i) => { result.push({ id: pageElement.get('id').value, label: pageElement.get('title').value, type: ToCEntryType.Page, form: pageElement, numbering: (i + 1).toString(), subEntriesType: ToCEntryType.Section } as ToCEntry) }); // build first level sections (this.form.get('sections') as FormArray).controls.forEach((sectionElement, i) => { const currentSectionPageId = sectionElement.get('page').value; const pageToAdd = result.filter(x => x.id == currentSectionPageId)[0]; if (pageToAdd.subEntries == null) pageToAdd.subEntries = []; const item = { id: sectionElement.get('id').value, label: sectionElement.get('title').value, type: ToCEntryType.Section, form: sectionElement, numbering: pageToAdd.numbering + '.' + (pageToAdd.subEntries.length +1) } as ToCEntry; const sectionItems = this.populateSections(sectionElement.get('sections') as FormArray, item.numbering); const fieldSetItems = this.populateFieldSets(sectionElement.get('fieldSets') as FormArray, item.numbering); if (sectionItems != null) { item.subEntries = sectionItems; item.subEntriesType = ToCEntryType.Section; } if (fieldSetItems != null) { if (item.subEntries == null) { item.subEntries = fieldSetItems; } else { item.subEntries.push(...fieldSetItems); } item.subEntriesType = ToCEntryType.FieldSet; } pageToAdd.subEntries.push(item); }); this._sortToCentries(result);//ordeby ordinal this._updateNumbering(result, '');//update nubering if needed return result; } private populateSections(sections: FormArray, existingNumbering: string): ToCEntry[] { if (sections == null || sections.controls == null || sections.controls.length == 0) { return null; } const result: ToCEntry[] = []; sections.controls.forEach((sectionElement, i) => { const item = { id: sectionElement.get('id').value, label: sectionElement.get('title').value, type: ToCEntryType.Section, form: sectionElement, numbering: existingNumbering + '.' + (i + 1) } as ToCEntry; const sectionItems = this.populateSections(sectionElement.get('sections') as FormArray, item.numbering); const fieldSetItems = this.populateFieldSets(sectionElement.get('fieldSets') as FormArray, item.numbering); if (sectionItems != null) { item.subEntries = sectionItems; item.subEntriesType = ToCEntryType.Section; } if (fieldSetItems != null) { if (item.subEntries == null) { item.subEntries = fieldSetItems; } else { item.subEntries.push(...fieldSetItems); } item.subEntriesType = ToCEntryType.FieldSet; } result.push(item); }); return result; } private populateFieldSets(fieldSets: FormArray, existingNumbering: string): ToCEntry[] { if (fieldSets == null || fieldSets.controls == null || fieldSets.controls.length == 0) { return null; } const result: ToCEntry[] = []; fieldSets.controls.forEach((fieldSetElement, i) => { result.push({ id: fieldSetElement.get('id').value, label: fieldSetElement.get('title').value, type: ToCEntryType.FieldSet, //subEntries: this.populateSections((fieldSetElement.get('fieldSets') as FormArray), existingNumbering + '.' + i), form: fieldSetElement, numbering: existingNumbering + '.' + (i + 1) } as ToCEntry) }); return result; } private _findTocEntryById(id: string, tocentries: ToCEntry[]): ToCEntry{ if(!tocentries || !tocentries.length){ 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; } addNewEntry(tce: NewEntryType) { const parent = tce.parent; //define entry type switch (tce.childType) { case ToCEntryType.Page: const pagesArray = (this.form.get('pages') as FormArray); const page: PageEditorModel = new PageEditorModel(pagesArray.length); const pageForm = page.buildForm(); // this.dataModel.pages.push(page); pagesArray.push(pageForm); // this.form.updateValueAndValidity(); this.refreshToCEntries(); this.selectedTocEntry = this._findTocEntryById(pageForm.get('id').value, this.toCEntries); break; case ToCEntryType.Section: const section: SectionEditorModel = new SectionEditorModel(); section.id = Guid.create().toString(); let sectionsArray:FormArray; if (parent.type === ToCEntryType.Page) {//FIRST LEVEL SECTION sectionsArray = this.form.get('sections') as FormArray; section.page = parent.id; try{ const max = sectionsArray.controls.filter(control=>control.get('page').value === parent.id) .map(control=>control.get('ordinal').value) .reduce((a,b)=>Math.max(a,b)); section.ordinal = max + 1; }catch{ 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; //adding page parent MAYBE NOT NEEDED section.page = parent.form.get('page').value; try{ const maxOrdinal = sectionsArray.controls.map(control=>control.get('ordinal').value).reduce((a,b)=>Math.max(a, b)); section.ordinal = maxOrdinal+1; }catch{ section.ordinal = sectionsArray.length; } sectionsArray.push(section.buildForm()); // (child.form.parent as FormArray).push(section.buildForm()); }else{ console.error('Section can only be child of a page or another section'); } const sectionAdded = sectionsArray.at(sectionsArray.length -1) as FormGroup; // sectionAdded.setValidators(this.customEditorValidators.sectionHasAtLeastOneChildOf('fieldSets','sections')); // sectionAdded.updateValueAndValidity(); this.refreshToCEntries(); this.selectedTocEntry = this._findTocEntryById(sectionAdded.get('id').value, this.toCEntries); break; case ToCEntryType.FieldSet: //create one field form fieldset const field: FieldEditorModel = new FieldEditorModel(); field.id = Guid.create().toString(); field.ordinal = 0;//first filed in the fields list const fieldForm = field.buildForm(); // fieldForm.setValidators(this.customFieldValidator()); // fieldForm.get('viewStyle').get('renderStyle').setValidators(Validators.required); // fieldSet.fields.push(field); // field.ordinal = fieldSet.fields.length-1; const fieldSetsArray = parent.form.get('fieldSets') as FormArray //give fieldset id and ordinal const fieldSet: FieldSetEditorModel = new FieldSetEditorModel(); const fieldSetId = Guid.create().toString(); fieldSet.id = fieldSetId; try{ const maxOrdinal = fieldSetsArray.controls.map(control=>control.get('ordinal').value).reduce((a,b)=>Math.max(a, b)); fieldSet.ordinal = maxOrdinal+1; }catch{ fieldSet.ordinal = fieldSetsArray.length; } const fieldsetForm = fieldSet.buildForm(); (fieldsetForm.get('fields') as FormArray).push(fieldForm); fieldSetsArray.push(fieldsetForm); this.refreshToCEntries(); this.selectedTocEntry = this._findTocEntryById(fieldSetId, this.toCEntries); // fieldForm.updateValueAndValidity(); break; default: break; } this.form.updateValueAndValidity(); } onRemoveEntry(tce: ToCEntry){ const dialogRef = this.dialog.open(ConfirmationDialogComponent, { restoreFocus: false, data: { message: this.language.instant('GENERAL.CONFIRMATION-DIALOG.DELETE-ITEM'), confirmButton: this.language.instant('GENERAL.CONFIRMATION-DIALOG.ACTIONS.CONFIRM'), cancelButton: this.language.instant('GENERAL.CONFIRMATION-DIALOG.ACTIONS.CANCEL'), isDeleteConfirmation: true } }); dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(result => { if (result) { this._deleteEntry(tce); } }); } private _deleteEntry(tce: ToCEntry) { //define entry type switch (tce.type) { case ToCEntryType.Page: const pages = this.form.get('pages') as FormArray; let pageIndex = -1; //get the index for (let i = 0; i < pages.length; i++) { let page = pages.at(i) as FormGroup; if (page.controls.id.value === tce.id) { pageIndex = i; break; } } if (pageIndex >= 0) { //remove page this._updateSelectedItem(tce); pages.removeAt(pageIndex); //clean up sections of removed page const sections = (this.form.get('sections') as FormArray); const sectionsIndexToBeRemoved = []; sections.controls.forEach((section,idx)=>{ if(section.get('page').value === tce.id){ sectionsIndexToBeRemoved.push(idx); } }); if(sectionsIndexToBeRemoved.length){ sectionsIndexToBeRemoved.reverse().forEach(index=>{ sections.removeAt(index); }); } //update page ordinals for(let i=0; i= 0) { //section found const sections = (this.form.get('sections') as FormArray); //remove section this._updateSelectedItem(tce); sections.removeAt(index); //update ordinal for(let i=0; i< sections.length; i++){ sections.at(i).get('ordinal').patchValue(i); } } else {//NOT FOUND IN FIRST LEVEL CASE //LOOK FOR SUBSECTION CASE let parentFormArray = tce.form.parent as FormArray; for (let i = 0; i < parentFormArray.length; i++) { let section = parentFormArray.at(i); if (section.get('id').value == tce.id) { index = i; break; } } if (index >= 0) { this._updateSelectedItem(tce); parentFormArray.removeAt(index); //update odrinal for(let i=0; i=0){//fieldset found this._updateSelectedItem(tce); parentFormArray.removeAt(idx); //patching order for(let i=0; i{ if(section.get('id').value === tce.id){ isFirstLevel = true; } }); let parentId = null; if(isFirstLevel){ parentId = tce.form.get('page').value; }else{ parentId = tce.form.parent.parent.get('id').value } // const parentId = tce.form.parent.parent.get('id').value; if(parentId){ const tocentries = this.getTocEntries(); const parent = this._findTocEntryById(parentId, tocentries); if(parent){ this.selectedTocEntry = parent; }else{ this.selectedTocEntry = null; } }else{ this.selectedTocEntry = null; } } } } } tocEntryIsChildOf(testingChild: ToCEntry,parent: ToCEntry): boolean{ if(!testingChild || !parent) return false; if(testingChild.id == parent.id){return true;} if(parent.subEntries){ let childFound:boolean = false; parent.subEntries.forEach(subEntry=>{ if(this.tocEntryIsChildOf(testingChild, subEntry)){ childFound = true; return true; } }) return childFound; } return false; } selectedTocEntry: ToCEntry displayItem(entry: ToCEntry): void { this.selectedTocEntry = entry; } get numOfPages(){ return (this.form.get('pages')).length; } // getSectionIndex(): number{ // // if(this.sectionIdPreviewed == null) return; // const valuesArray = this.form.get('sections').value; // let currentVal = this.sectionIdPreviewed; // let indexArray:string[] = valuesArray.map(element=> element.page); // let index = indexArray.indexOf(currentVal); // console.log(index); // return index ? index :-1; // } // getCurrentEditorModel(): SectionEditorModel{ // let currentEditor = this.dataModel.sections.filter(section=> section.page == this.sectionIdPreviewed)[0]; // return currentEditor; // } // subForm(){ // const valuesArray = this.form.get('sections').value; // let currentVal = this.sectionIdPreviewed; // let indexArray:string[] = valuesArray.map(element=> element.page); // let index = indexArray.indexOf(currentVal); // let subForm = (this.form.get('sections') as FormArray).controls[index]; // console.log(subForm); // return subForm; // } getFieldTile(formGroup: FormGroup, index: number) { if (formGroup.get('title') && formGroup.get('title').value && formGroup.get('title').value.length > 0) { return formGroup.get('title').value; } return "Field " + (index + 1); } deleteFieldSet(formArray: FormArray, index: number) { formArray.removeAt(index); } printForm(){ // console.log(this.form.value); console.log(this.form); } get barPercentage(){ if(!this.stepper || !this.steps){ return 0; } const selectedIndex = this.stepper.selectedIndex + 1; return (selectedIndex / this.stepper.steps.length) * 100; } get progressStyle(){ // return {'transform': 'translateX('+this.barPercentage+'%) skewX(-25deg)'} const diff = 3; return { 'clip-path': `polygon(0 0, ${Math.round(this.barPercentage + diff)}% 0, ${Math.round(this.barPercentage)}% 100%, 0 100%)` } } steps:QueryList; // get steps(){ // if(!this.stepper){ // return []; // } // return this.stepper.steps; // } // generatePreviewForm(){ // const model = new DatasetDescriptionFormEditorModel(); // const toCentries = this.getTocEntries(); // //first level is always pages // model.pages = toCentries.map((entry,idx)=>{ // if( !(entry.type == ToCEntryType.Page)){ // return null; // } // const pageModel = new DatasetDescriptionPageEditorModel(); // pageModel.ordinal = entry.form.get('ordinal').value; // pageModel.title = entry.label; // if(entry.subEntries){ // pageModel.sections = entry.subEntries.map(section=>{ // const sectionModel = new DatasetDescriptionSectionEditorModel(); // sectionModel.id = section.id; // sectionModel.ordinal = section.form.get('ordinal').value; // sectionModel.description = section.form.get('description').value; // sectionModel.page = entry.form.get('ordinal').value; // sectionModel.title = section.label; // sectionModel.numbering = (idx+1).toString(); // if(section.subEntriesType == ToCEntryType.Section){ // sectionModel.sections = this._buildSectionsRecursively(section.subEntries, sectionModel.numbering); // }else{ // sectionModel.compositeFields = this._buildFormFields(section.subEntries, sectionModel.numbering) // } // return sectionModel; // }); // }; // return pageModel; // }); // //populate rules // const rules:Rule[] =[]; // const fieldSets = this._getFieldSets(toCentries); // fieldSets.forEach(fs=>{ // const fields = fs.form.get('fields') as FormArray; // if(fields){ // fields.controls.forEach(field=>{ // const rulesArray = field.get('visible').get('rules').value; // if(rulesArray){ // rulesArray.forEach(ruleElement => { // const rule: Rule = new Rule(); // rule.targetField = ruleElement.target; // rule.sourceField = field.get('id').value; // rule.requiredValue = ruleElement.value; // rules.push(rule); // }); // } // }); // } // }); // model.rules = rules; // this.visibilityRules = rules; // this.previewForm = model.buildForm(); // } updateVisibilityRules(){ const rules:Rule[] =[]; const fieldSets = this._getFieldSets(this.getTocEntries()); fieldSets.forEach(fs=>{ const fields = fs.form.get('fields') as FormArray; if(fields){ fields.controls.forEach(field=>{ const rulesArray = field.get('visible').get('rules').value; if(rulesArray){ rulesArray.forEach(ruleElement => { const rule: Rule = new Rule(); rule.targetField = ruleElement.target; rule.sourceField = field.get('id').value; rule.requiredValue = ruleElement.value; rules.push(rule); }); } }); } }); this.visibilityRules = rules; } visibilityRules:Rule[]; private _buildSectionsRecursively( tocentries: ToCEntry[], parentNumbering:string): DatasetDescriptionSectionEditorModel[]{ if(!tocentries) return null; const result: Array = []; tocentries.forEach((tocentry, idx)=>{ const sectionModel = new DatasetDescriptionSectionEditorModel(); sectionModel.id = tocentry.id; sectionModel.ordinal = tocentry.form.get('ordinal').value; sectionModel.description = tocentry.form.get('description').value; // sectionModel.page = entry.form.get('ordinal').value; sectionModel.title = tocentry.label; // sectionModel.numbering = tocentry.numbering; sectionModel.numbering = parentNumbering+"."+(idx+1);; if(tocentry.subEntriesType == ToCEntryType.Section){ sectionModel.sections = this._buildSectionsRecursively(tocentry.subEntries, sectionModel.numbering); }else{ sectionModel.compositeFields = this._buildFormFields(tocentry.subEntries, sectionModel.numbering); } result.push(sectionModel); }) return result; } private _buildFormFields(tocentries: ToCEntry[], parentNumbering: string):DatasetDescriptionCompositeFieldEditorModel[]{ if(!tocentries) return null; const fieldsets:DatasetDescriptionCompositeFieldEditorModel[] = []; tocentries.forEach((fs, idx)=>{ const fieldset = new DatasetDescriptionCompositeFieldEditorModel(); fieldset.description = fs.form.get('description').value; fieldset.extendedDescription = fs.form.get('extendedDescription').value; fieldset.id = fs.form.get('id').value; fieldset.multiplicity = fs.form.get('multiplicity').value; fieldset.additionalInformation = fs.form.get('additionalInformation').value; fieldset.ordinal = fs.form.get('ordinal').value; // fieldset.numbering = fs.numbering; fieldset.numbering = parentNumbering+"."+(idx+1); fieldset.hasCommentField = fs.form.get('hasCommentField').value; fieldset.title = fs.label; // fieldset.fields = (fs.form.get('fields') as FormArray).getRawValue(); fieldset.fields = (fs.form.get('fields') as FormArray).controls.map(field=>{ const fieldModel = new DatasetDescriptionFieldEditorModel(); fieldModel.data = (field.get('data') as FormGroup).getRawValue(); fieldModel.id = field.get('id').value; fieldModel.viewStyle = (field.get('viewStyle') as FormGroup).getRawValue(); // fieldModel.defaultValue = (field.get('defaultValue') as FormGroup).getRawValue(); fieldModel.value = (field.get('defaultValue') as FormGroup).get('value').value; fieldModel.defaultValue = fieldModel.value; fieldModel.page = field.get('page').value; fieldModel.validations = field.get('validations').value; return fieldModel; }); fieldsets.push(fieldset); }); return fieldsets; } private _getFieldSets(tocentries: ToCEntry[]):ToCEntry[]{ const fieldsets:ToCEntry[] = []; if(!tocentries) return fieldsets; tocentries.forEach(entry=>{ if(entry.type == ToCEntryType.FieldSet){ fieldsets.push(entry); }else{ fieldsets.push(...this._getFieldSets(entry.subEntries)); } }); return fieldsets; } // get basicInfo(){ // 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 // }) // return fg; // } onMatStepperSelectionChange(event: StepperSelectionEvent){ if(event.selectedIndex === (this.steps.length -1)){//preview selected // this.generatePreviewForm();//TODO LAZY LOADING IN THE TEMPLATE this.getPreview(); }else{ // this.previewForm = null; this.formGroup = null; } this.form.markAsUntouched(); } // previewForm:FormGroup; onDataNeedsRefresh(params?){ const tocentries = this.refreshToCEntries(); if(params && params.draggedItemId){ if(params.draggedItemId){ this.displayItem(this._findTocEntryById(params.draggedItemId, tocentries)); } } } cloneFieldSet(fieldset: FormGroup){ const values = fieldset.getRawValue(); const parentArray = fieldset.parent as FormArray; values.id = Guid.create().toString(); values.ordinal = parentArray.length; values.fields.forEach(element => { element.id = Guid.create().toString() }); const clonedModel = new FieldSetEditorModel().fromModel(values); const clonedForm = clonedModel.buildForm(); parentArray.push(clonedForm); //update tocentries and make selected tocentry the cloedn let entries = this.refreshToCEntries(); const entryfound = this._findTocEntryById(clonedForm.get('id').value, entries); if(entryfound){ this.selectedTocEntry = entryfound; } // //create one field form fieldset // const field: FieldEditorModel = new FieldEditorModel(); //to ask // field.id = Guid.create().toString(); // field.ordinal = 0;//first filed in the fields list // fieldSet.fields.push(field); // // field.ordinal = fieldSet.fields.length-1; // //give fieldset id and ordinal // fieldSet.id = Guid.create().toString(); // fieldSet.ordinal = (parent.form.get('fieldSets') as FormArray).length; // (parent.form.get('fieldSets')).push(fieldSet.buildForm()); // // const parentArray = parent.form.get('fieldSets') as FormArray; // const addedFieldSet = parentArray.at(parentArray.length - 1); } isStepCompleted(stepIndex: number){ let stepCompleted = false; this.steps.forEach((step,index)=>{ if(stepIndex === index){ stepCompleted = step.completed; } }); return stepCompleted; } 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.completed){ stepUnlocked = true; } } }); return stepUnlocked; } validateStep(selectedIndex){ if(selectedIndex === 1){//form description if(this.form.invalid){ this.checkFormValidation(); } } // if(step.hasError){ // this.printMyErrors(this.form); // } } // 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]); // }); // } // }); // if(this.form.invalid){ // console.log('this form is invalid!'); // console.log(this.form.errors); // } // } private _buildErrorMessage(errors, numbering, key):string[]{ const errmess: string[] = []; Object.keys(errors).forEach(keyError => { switch(keyError){ case EditorCustomValidatorsEnum.atLeastOneSectionInPage: errmess.push( this.language.instant('DATASET-PROFILE-EDITOR.STEPS.FORM.FORM-VALIDATION.ERROR-MESSAGES.PAGE-MUST-HAVE-SECTION')); break; case EditorCustomValidatorsEnum.emptyArray: errmess.push(numbering+this.language.instant('DATASET-PROFILE-EDITOR.STEPS.FORM.FORM-VALIDATION.ERROR-MESSAGES.NEEDS-MORE-INFORMATION')) break; case EditorCustomValidatorsEnum.sectionMustHaveOneChild: errmess.push(numbering+this.language.instant('DATASET-PROFILE-EDITOR.STEPS.FORM.FORM-VALIDATION.ERROR-MESSAGES.MUST-HAVE-SECTION-OR-FIELDSET')) 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; } printMyErrors(form: AbstractControl){ // this._printToCentriesErrrors(this.toCEntries); const result = this._getErrors(form); // console.log('got errors '); // console.log(result); if(result && form.invalid){ const errmess:string[] = []; 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(`${this.language.instant('DATASET-PROFILE-EDITOR.STEPS.FORM.FORM-VALIDATION.ERROR-MESSAGES.MISSING')} ${err.key}` ); }else{ errmess.push(this.language.instant('DATASET-PROFILE-EDITOR.STEPS.FORM.FORM-VALIDATION.ERROR-MESSAGES.PROVIDE-PAGE-AND-SECTION')); } // errmess.push(...this._buildErrorMessage(err.errors,"", err.key) ); }) } const dialogRef = this.dialog.open(FormValidationErrorsDialogComponent, { disableClose: true, autoFocus: false, restoreFocus: false, data: { errorMessages: errmess, projectOnly: false }, }); } } /** * Get all filedsets in a tocentry array; * @param entries Tocentries to search in * @returns The tocentries that are Fieldsets provided in the entries */ private _getAllFieldSets(entries: ToCEntry[]):ToCEntry[]{ const fieldsets:ToCEntry[] = []; if(!entries || !entries.length) return fieldsets; entries.forEach(e=>{ if(e.type === ToCEntryType.FieldSet){ fieldsets.push(e); }else{ fieldsets.push(...this._getAllFieldSets(e.subEntries)); } }); return fieldsets; } private _getErrors(aControl: AbstractControl):InvalidControl[]{ if(aControl.valid) return; let controlType = 'control'; 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.invalid) return; //// !!!!! Important to be !invalid. (In case the template is finalized) if(control instanceof FormControl){ const ctrl = control as FormControl; invalidControls.push({ errors:ctrl.errors, id: ctrl.get('id')? ctrl.get('id').value: null, invalidSubControls: [], key: key }); }else{ // if(aControl.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 // }); // } //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': // 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; } invalidControls.forEach(e=>{ if(!e.id){ e.id = aControl.get('id')? aControl.get('id').value : null; } }) return invalidControls; } //Temporary patch /** * Append custom validators to fields. Some validators are applied on template. In case they are never rendereed, * he form might be valid when it shouldnt. * @param */ private _initializeFormValidity(tocentries: ToCEntry[]) { const fieldsets = this._getAllFieldSets(tocentries); try{ fieldsets.forEach(fs=>{ fs.form.get('title').setValidators(Validators.required); const fieldsF = fs.form.get('fields') as FormArray; if(fieldsF){ fieldsF.controls.forEach(field=>{ const renderStyleValue = field.get('viewStyle').get('renderStyle').value; if(renderStyleValue === DatasetProfileFieldViewStyle.CheckBox){ field.get('defaultValue').get('value').setValidators(Validators.required); }else if(renderStyleValue === 'combobox'){ const comboType = field.get('data').get('type').value; if(comboType === DatasetProfileComboBoxType.Autocomplete){//As 'Other' in UI field.get('data').setValidators(EditorCustomValidators.atLeastOneElementListValidator('autoCompleteSingleDataList')); }else if(comboType === DatasetProfileComboBoxType.WordList){ field.get('data').setValidators(EditorCustomValidators.atLeastOneElementListValidator('options')); } }else if(renderStyleValue === DatasetProfileFieldViewStyle.RadioBox){ field.get('data').setValidators(EditorCustomValidators.atLeastOneElementListValidator('options')); } }); } }); }catch(e){ console.error('Error initializing validators.'); console.error(e); } } } interface InvalidControl{ key: string, errors: any, id: string, invalidSubControls: InvalidControl[] }