From 1dcfad9c699fe17edab1a1379db46c80c94a2415 Mon Sep 17 00:00:00 2001 From: jojohoch <joachim.hoch@iqb.hu-berlin.de> Date: Wed, 28 Jul 2021 09:34:59 +0200 Subject: [PATCH] [player] Show different validation warnings * Move setting of validators from `FormElementComponent` to `ValidationMessageComponent` * Add validators and warnings to `ValidationMessageComponent` * Move subscriptions for form controls and their validators from `PageComponent` to `FormComponent` --- .../form-element-component.directive.ts | 18 +---- projects/common/form.service.ts | 2 +- .../src/app/components/form.component.ts | 19 ++++- .../src/app/components/page.component.ts | 38 +-------- .../validation-message.component.ts | 77 +++++++++++++++++-- 5 files changed, 92 insertions(+), 62 deletions(-) diff --git a/projects/common/form-element-component.directive.ts b/projects/common/form-element-component.directive.ts index c57858693..90efb4720 100644 --- a/projects/common/form-element-component.directive.ts +++ b/projects/common/form-element-component.directive.ts @@ -2,7 +2,7 @@ import { Directive, EventEmitter, OnDestroy, OnInit, Output } from '@angular/core'; import { - FormControl, FormGroup, ValidatorFn, Validators + FormControl, FormGroup } from '@angular/forms'; import { Subject } from 'rxjs'; import { pairwise, startWith, takeUntil } from 'rxjs/operators'; @@ -40,22 +40,6 @@ export abstract class FormElementComponent extends ElementComponent implements O this.formValueChanged.emit({ id: this.elementModel.id, values: [prevValue, nextValue] }); } }); - // TODO: find better solution - setTimeout((): void => { - this.formService.setValidators({ - id: this.elementModel.id, - validators: this.validators, - formGroup: this.parentForm - }); - }); - } - - private get validators(): ValidatorFn[] { - const validators: ValidatorFn[] = []; - if (this.elementModel.required) { - validators.push(Validators.required); - } - return validators; } private get formControl(): FormControl { diff --git a/projects/common/form.service.ts b/projects/common/form.service.ts index e8633688c..ba2ef1355 100644 --- a/projects/common/form.service.ts +++ b/projects/common/form.service.ts @@ -42,6 +42,6 @@ export class FormService { } setValidators(validations: FormControlValidators): void { - this._validationsAdded.next(validations); + Promise.resolve().then(() => this._validationsAdded.next(validations)); } } diff --git a/projects/player/src/app/components/form.component.ts b/projects/player/src/app/components/form.component.ts index b4dce6d80..0933e2355 100644 --- a/projects/player/src/app/components/form.component.ts +++ b/projects/player/src/app/components/form.component.ts @@ -5,7 +5,9 @@ import { takeUntil } from 'rxjs/operators'; import { FormService } from '../../../../common/form.service'; import { VeronaSubscriptionService } from '../services/verona-subscription.service'; import { VeronaPostService } from '../services/verona-post.service'; -import { FormGroupPage, ValueChangeElement } from '../../../../common/form'; +import { + FormControlElement, FormControlValidators, FormGroupPage, ValueChangeElement +} from '../../../../common/form'; import { PlayerConfig, UnitState, VopNavigationDeniedNotification } from '../models/verona'; @@ -50,6 +52,12 @@ export class FormComponent implements OnDestroy { this.formService.groupAdded .pipe(takeUntil(this.ngUnsubscribe)) .subscribe((group: FormGroupPage): void => this.addGroup(group)); + this.formService.controlAdded.pipe( + takeUntil(this.ngUnsubscribe) + ).subscribe((control: FormControlElement): void => this.addControl(control)); + this.formService.validationsAdded.pipe( + takeUntil(this.ngUnsubscribe) + ).subscribe((validations: FormControlValidators): void => this.setValidators(validations)); this.veronaSubscriptionService.vopNavigationDeniedNotification .pipe(takeUntil(this.ngUnsubscribe)) .subscribe((message: VopNavigationDeniedNotification): void => this.onNavigationDenied(message)); @@ -58,6 +66,15 @@ export class FormComponent implements OnDestroy { .subscribe((formValues: { pages: Record<string, string>[] }): void => this.onFormChanges(formValues)); } + private addControl = (control: FormControlElement): void => { + control.formGroup.addControl(control.id, control.formControl); + }; + + private setValidators = (validators: FormControlValidators): void => { + validators.formGroup.controls[validators.id].setValidators(validators.validators); + validators.formGroup.controls[validators.id].updateValueAndValidity(); + }; + private onNavigationDenied(message: VopNavigationDeniedNotification): void { // eslint-disable-next-line no-console console.log('player: onNavigationDenied', message); diff --git a/projects/player/src/app/components/page.component.ts b/projects/player/src/app/components/page.component.ts index b8e18db44..d5f983d49 100644 --- a/projects/player/src/app/components/page.component.ts +++ b/projects/player/src/app/components/page.component.ts @@ -1,12 +1,9 @@ import { - Component, Input, OnDestroy, OnInit + Component, Input, OnInit } from '@angular/core'; import { FormGroup } from '@angular/forms'; -import { takeUntil } from 'rxjs/operators'; -import { Subject } from 'rxjs'; import { UnitPage } from '../../../../common/unit'; import { FormService } from '../../../../common/form.service'; -import { FormControlElement, FormControlValidators } from '../../../../common/form'; @Component({ selector: 'app-page', @@ -25,46 +22,15 @@ import { FormControlElement, FormControlValidators } from '../../../../common/fo ` }) -export class PageComponent implements OnInit, OnDestroy { +export class PageComponent implements OnInit { @Input() page!: UnitPage; @Input() parentForm!: FormGroup; pageForm!: FormGroup; - private ngUnsubscribe = new Subject<void>(); constructor(private formService: FormService) {} ngOnInit(): void { this.pageForm = new FormGroup({}); this.formService.registerFormGroup({ id: this.page.id, formGroup: this.pageForm }); - this.initSubscriptions(); - } - - private initSubscriptions(): void { - this.formService.controlAdded.pipe( - takeUntil(this.ngUnsubscribe) - ).subscribe((control: FormControlElement): void => this.addControl(control)); - this.formService.validationsAdded.pipe( - takeUntil(this.ngUnsubscribe) - ).subscribe((validations: FormControlValidators): void => this.setValidators(validations)); - } - - private addControl(control: FormControlElement): void { - // we need to check that the control belongs to the page - if (this.pageForm === control.formGroup) { - this.pageForm.addControl(control.id, control.formControl); - } - } - - private setValidators(validators: FormControlValidators): void { - // we need to check that the control belongs to the page - if (this.pageForm === validators.formGroup) { - this.pageForm.controls[validators.id].setValidators(validators.validators); - this.pageForm.controls[validators.id].updateValueAndValidity(); - } - } - - ngOnDestroy(): void { - this.ngUnsubscribe.next(); - this.ngUnsubscribe.complete(); } } diff --git a/projects/player/src/app/components/validation-message.component.ts b/projects/player/src/app/components/validation-message.component.ts index 59e39b9d1..8bf53ae60 100644 --- a/projects/player/src/app/components/validation-message.component.ts +++ b/projects/player/src/app/components/validation-message.component.ts @@ -1,13 +1,32 @@ import { Component, OnInit } from '@angular/core'; -import { FormControl, FormGroup } from '@angular/forms'; -import { InputUIElement, UnitUIElement } from '../../../../common/unit'; +import { + FormControl, FormGroup, ValidatorFn, Validators +} from '@angular/forms'; +import { + InputUIElement, NumberFieldElement, TextFieldElement, UnitUIElement +} from '../../../../common/unit'; +import { FormService } from '../../../../common/form.service'; @Component({ selector: 'app-error-message', template: ` - <mat-error *ngIf="formElementControl && formElementControl.touched && formElementControl.errors"> - {{requiredMessage}} - </mat-error> + <ng-container *ngIf="formElementControl && formElementControl.touched"> + <mat-error *ngIf="formElementControl.errors?.required"> + {{requiredMessage}} + </mat-error> + <mat-error *ngIf="formElementControl.errors?.minlength"> + {{minLengthMessage}} + </mat-error> + <mat-error *ngIf="formElementControl.errors?.maxlength"> + {{maxLengthMessage}} + </mat-error> + <mat-error *ngIf="formElementControl.errors?.min"> + {{minMessage}} + </mat-error> + <mat-error *ngIf="formElementControl.errors?.max"> + {{maxMessage}} + </mat-error> + </ng-container> ` }) @@ -16,12 +35,56 @@ export class ValidationMessageComponent implements OnInit { parentForm!: FormGroup; formElementControl!: FormControl; + constructor(private formService: FormService) {} + ngOnInit(): void { this.formElementControl = this.parentForm.controls[this.elementModel.id] as FormControl; + this.formService.setValidators({ + id: this.elementModel.id, + validators: this.validators, + formGroup: this.parentForm + }); + } + + private get validators(): ValidatorFn[] { + const validators: ValidatorFn[] = []; + if (this.elementModel.required) { + validators.push(Validators.required); + } + if (this.elementModel.min) { + if (this.elementModel.type === 'number_field') { + validators.push(Validators.min(<number> this.elementModel.min)); + } else { + validators.push(Validators.minLength(<number> this.elementModel.min)); + } + } + if (this.elementModel.max) { + if (this.elementModel.type === 'number_field') { + validators.push(Validators.max(<number> this.elementModel.max)); + } else { + validators.push(Validators.maxLength(<number> this.elementModel.maxLength)); + } + } + return validators; } - // eslint-disable-next-line class-methods-use-this get requiredMessage(): string { - return (this.elementModel as InputUIElement).validationWarnMessage || 'Wert muss angegeben werden'; + return (this.elementModel as InputUIElement).requiredWarnMessage || 'Eingabe erforderlich'; + } + + get minLengthMessage(): string { + return (this.elementModel as TextFieldElement).minWarnMessage || 'Eingabe zu kurz'; + } + + get maxLengthMessage(): string { + return (this.elementModel as TextFieldElement).maxWarnMessage || 'Eingabe zu lang'; + } + + get minMessage(): string { + return (this.elementModel as NumberFieldElement).minWarnMessage || 'Wert zu klein'; + } + + get maxMessage(): string { + return (this.elementModel as NumberFieldElement).maxWarnMessage || 'Wert zu groß'; } } -- GitLab