diff --git a/src/app/shared/dialogs/dialogs-form.component.ts b/src/app/shared/dialogs/dialogs-form.component.ts index a49fa3b643..80f8700190 100644 --- a/src/app/shared/dialogs/dialogs-form.component.ts +++ b/src/app/shared/dialogs/dialogs-form.component.ts @@ -1,12 +1,22 @@ import { Component, Inject } from '@angular/core'; import { - MatLegacyDialog as MatDialog, MatLegacyDialogRef as MatDialogRef, MAT_LEGACY_DIALOG_DATA as MAT_DIALOG_DATA + MatLegacyDialog as MatDialog, + MatLegacyDialogRef as MatDialogRef, + MAT_LEGACY_DIALOG_DATA as MAT_DIALOG_DATA } from '@angular/material/legacy-dialog'; -import { UntypedFormGroup, UntypedFormBuilder } from '@angular/forms'; +import { FormArray, FormBuilder, FormGroup } from '@angular/forms'; import { DialogsLoadingService } from './dialogs-loading.service'; import { DialogsListService } from './dialogs-list.service'; import { DialogsListComponent } from './dialogs-list.component'; import { UserService } from '../user.service'; +import { + DialogField, + DialogFormControls, + DialogFormGroup, + DialogFormGroupConfig, + DialogFormValueMap, + DialogsFormData +} from './dialogs-form.service'; @Component({ templateUrl: './dialogs-form.component.html', @@ -27,19 +37,20 @@ import { UserService } from '../user.service'; export class DialogsFormComponent { public title: string; - public fields: any; - public modalForm: UntypedFormGroup; - passwordVisibility = new Map(); + public fields: DialogField[]; + public modalForm: DialogFormGroup; + passwordVisibility = new Map(); isSpinnerOk = true; errorMessage = ''; dialogListRef: MatDialogRef; disableIfInvalid = false; - private markFormAsTouched (formGroup: UntypedFormGroup) { - (Object).values(formGroup.controls).forEach(control => { - control.markAsTouched(); - if (control.controls) { - this.markFormAsTouched(control); + private markFormAsTouched(control: DialogFormGroup | FormArray) { + const controls = control instanceof FormGroup ? Object.values(control.controls) : control.controls; + controls.forEach(innerControl => { + innerControl.markAsTouched(); + if (innerControl instanceof FormGroup || innerControl instanceof FormArray) { + this.markFormAsTouched(innerControl); } }); } @@ -47,16 +58,19 @@ export class DialogsFormComponent { constructor( public dialogRef: MatDialogRef, private dialog: MatDialog, - private fb: UntypedFormBuilder, - @Inject(MAT_DIALOG_DATA) public data, + private fb: FormBuilder, + @Inject(MAT_DIALOG_DATA) public data: DialogsFormData, private dialogsLoadingService: DialogsLoadingService, private dialogsListService: DialogsListService, private userService: UserService ) { if (this.data && this.data.formGroup) { - this.modalForm = this.data.formGroup instanceof UntypedFormGroup ? - this.data.formGroup : - this.fb.group(this.data.formGroup, this.data.formOptions || {}); + this.modalForm = this.data.formGroup instanceof FormGroup ? + this.data.formGroup as DialogFormGroup : + this.fb.group>( + this.data.formGroup as DialogFormGroupConfig, + this.data.formOptions || {} + ); this.title = this.data.title; this.fields = this.data.fields.filter(field => !field.planetBeta || this.userService.isBetaEnabled()); this.isSpinnerOk = false; @@ -69,14 +83,14 @@ export class DialogsFormComponent { } } - onSubmit(mForm, dialog) { + onSubmit(mForm: DialogFormGroup, dialog: MatDialogRef) { if (!mForm.valid) { this.markFormAsTouched(mForm); return; } if (this.data && this.data.onSubmit) { this.dialogsLoadingService.start(); - this.data.onSubmit(mForm.value, mForm); + this.data.onSubmit(mForm.value as DialogFormValueMap, mForm); } if (!this.data || this.data.closeOnSubmit === true) { this.dialogsLoadingService.stop(); @@ -84,26 +98,29 @@ export class DialogsFormComponent { } } - togglePasswordVisibility(fieldName) { + togglePasswordVisibility(fieldName: string) { const visibility = this.passwordVisibility.get(fieldName) || false; this.passwordVisibility.set(fieldName, !visibility); } - openDialog(field) { - const initialSelection = this.modalForm.controls[field.name].value.map((value: any) => value._id); + openDialog(field: DialogField) { + const control = this.modalForm.controls[field.name]; + const currentValue = control.value as Array<{ _id: string }> | null; + const initialSelection = (currentValue || []).map((value) => value._id); this.dialogsLoadingService.start(); - this.dialogsListService.attachDocsData(field.db, 'title', this.dialogOkClick(field).bind(this), initialSelection).subscribe((data) => { - this.dialogsLoadingService.stop(); - this.dialogListRef = this.dialog.open(DialogsListComponent, { - data: data, - maxHeight: '500px', - width: '600px', - autoFocus: false + this.dialogsListService.attachDocsData(field.db, 'title', this.dialogOkClick(field).bind(this), initialSelection) + .subscribe((data) => { + this.dialogsLoadingService.stop(); + this.dialogListRef = this.dialog.open(DialogsListComponent, { + data: data, + maxHeight: '500px', + width: '600px', + autoFocus: false + }); }); - }); } - dialogOkClick(field) { + dialogOkClick(field: DialogField) { return (selection) => { this.modalForm.controls[field.name].setValue(selection); this.dialogListRef.close(); diff --git a/src/app/shared/dialogs/dialogs-form.service.ts b/src/app/shared/dialogs/dialogs-form.service.ts index 0ab0377378..2b2c02fd1e 100644 --- a/src/app/shared/dialogs/dialogs-form.service.ts +++ b/src/app/shared/dialogs/dialogs-form.service.ts @@ -3,34 +3,125 @@ import { DialogsFormComponent } from './dialogs-form.component'; import { MatLegacyDialogRef as MatDialogRef, MatLegacyDialog as MatDialog } from '@angular/material/legacy-dialog'; import { Injectable } from '@angular/core'; import { - UntypedFormBuilder, - UntypedFormGroup + AbstractControlOptions, + AsyncValidatorFn, + FormArray, + FormBuilder, + FormControl, + FormControlState, + FormGroup, + ValidatorFn } from '@angular/forms'; +export type DialogFieldType = + | 'checkbox' + | 'textbox' + | 'password' + | 'selectbox' + | 'radio' + | 'rating' + | 'textarea' + | 'markdown' + | 'dialog' + | 'date' + | 'time' + | 'toggle'; + +export interface DialogField { + name: TName; + type: DialogFieldType; + placeholder?: string; + label?: string; + required?: boolean; + disabled?: boolean; + multiple?: boolean; + options?: Array<{ name: string; value?: unknown } | string>; + planetBeta?: boolean; + tooltip?: string; + reset?: boolean; + text?: string; + authorizedRoles?: string | string[]; + imageGroup?: unknown; + db?: string; + [key: string]: unknown; +} + +export interface DialogFormValueMap { + [key: string]: unknown; +} + +type DialogControlConfig = + | TValue + | FormControlState + | FormControl + | FormGroup + | FormArray + | [TValue | FormControlState, (ValidatorFn | ValidatorFn[] | null)?, (AsyncValidatorFn | AsyncValidatorFn[] | null)?]; + +export type DialogFormGroupConfig = { + [K in keyof T]: DialogControlConfig; +}; + +export type DialogFormControls = { + [K in keyof T]: FormControl | FormGroup | FormArray; +}; + +export type DialogFormGroup = FormGroup>; + +export type DialogFormGroupInput = + | DialogFormGroup + | DialogFormGroupConfig; + +export interface DialogsFormOptions { + autoFocus?: boolean; + disableIfInvalid?: boolean; + onSubmit?: (value: T, form: DialogFormGroup) => void; + formOptions?: AbstractControlOptions; + closeOnSubmit?: boolean; + [key: string]: unknown; +} + +export interface DialogsFormData + extends DialogsFormOptions { + title: string; + fields: DialogField[]; + formGroup: DialogFormGroupInput; +} + @Injectable() export class DialogsFormService { private dialogRef: MatDialogRef; - constructor(private dialog: MatDialog, private fb: UntypedFormBuilder) { } + constructor(private dialog: MatDialog, private fb: FormBuilder) { } - public confirm(title: string, fields: any, formGroup: any, autoFocus = false): Observable { + public confirm( + title: string, + fields: DialogField[], + formGroup: DialogFormGroupInput, + autoFocus = false + ): Observable { let dialogRef: MatDialogRef; dialogRef = this.dialog.open(DialogsFormComponent, { width: '600px', autoFocus: autoFocus }); - if (formGroup instanceof UntypedFormGroup) { + if (formGroup instanceof FormGroup) { dialogRef.componentInstance.modalForm = formGroup; } else { - dialogRef.componentInstance.modalForm = this.fb.group(formGroup); + dialogRef.componentInstance.modalForm = this.fb.group>(formGroup); } dialogRef.componentInstance.title = title; dialogRef.componentInstance.fields = fields; - return dialogRef.afterClosed(); + return dialogRef.afterClosed() as Observable; } - openDialogsForm(title: string, fields: any[], formGroup: any, options: any) { + openDialogsForm( + title: string, + fields: DialogField[], + formGroup: DialogFormGroupInput, + options: DialogsFormOptions = {} + ) { this.dialogRef = this.dialog.open(DialogsFormComponent, { width: '600px', autoFocus: options.autoFocus,