From 4005b51e990dafd956ed0952b8829e9e6ef70579 Mon Sep 17 00:00:00 2001
From: jojohoch <joachim.hoch@iqb.hu-berlin.de>
Date: Thu, 30 Sep 2021 16:03:39 +0200
Subject: [PATCH] [player] Refactor validation messages

* Delete validation message component
* Use instead `mat-error` inside element components to take advantage
 of `mat-input-field`
* Add get validators method to each form element component
* Display validation messages via `error-transform` pipe
---
 projects/common/app.module.ts                 |  6 +-
 projects/common/assets/i18n/de.json           |  9 ++-
 .../element-components/checkbox.component.ts  | 41 +++++++---
 .../correction.component.ts                   |  9 +++
 .../element-components/dropdown.component.ts  | 50 +++++++-----
 .../pipes/error-transform.pipe.ts             | 46 +++++++++++
 .../pipes/safe-resource-url.pipe.ts           |  0
 .../radio-button-group.component.ts           | 16 +++-
 .../element-components/text-area.component.ts | 26 ++++--
 .../text-field.component.ts                   | 21 +++++
 .../form-element-component.directive.ts       |  9 ++-
 projects/common/form.service.ts               |  4 +-
 projects/player/src/app/app.module.ts         |  2 -
 .../components/element/element.component.html |  6 +-
 .../validation-message.component.css          |  9 ---
 .../validation-message.component.html         | 22 -----
 .../validation-message.component.ts           | 80 -------------------
 projects/player/src/assets/i18n/de.json       |  7 --
 18 files changed, 192 insertions(+), 171 deletions(-)
 create mode 100644 projects/common/element-components/pipes/error-transform.pipe.ts
 rename projects/common/{ => element-components}/pipes/safe-resource-url.pipe.ts (100%)
 delete mode 100644 projects/player/src/app/components/validation-message/validation-message.component.css
 delete mode 100644 projects/player/src/app/components/validation-message/validation-message.component.html
 delete mode 100644 projects/player/src/app/components/validation-message/validation-message.component.ts

diff --git a/projects/common/app.module.ts b/projects/common/app.module.ts
index f708af255..5cf5531e4 100644
--- a/projects/common/app.module.ts
+++ b/projects/common/app.module.ts
@@ -32,8 +32,9 @@ import { ImageComponent } from './element-components/image.component';
 import { VideoComponent } from './element-components/video.component';
 import { AudioComponent } from './element-components/audio.component';
 import { CorrectionComponent } from './element-components/compound-components/correction.component';
-import { SafeResourceUrlPipe } from './pipes/safe-resource-url.pipe';
+import { SafeResourceUrlPipe } from './element-components/pipes/safe-resource-url.pipe';
 import { InputBackgroundColorDirective } from './element-components/directives/input-background-color.directive';
+import { ErrorTransformPipe } from './element-components/pipes/error-transform.pipe';
 
 @NgModule({
   imports: [
@@ -63,7 +64,8 @@ import { InputBackgroundColorDirective } from './element-components/directives/i
     DropdownComponent,
     CorrectionComponent,
     SafeResourceUrlPipe,
-    InputBackgroundColorDirective
+    InputBackgroundColorDirective,
+    ErrorTransformPipe
   ],
   exports: [
     CommonModule,
diff --git a/projects/common/assets/i18n/de.json b/projects/common/assets/i18n/de.json
index 5b5eee082..52227b038 100644
--- a/projects/common/assets/i18n/de.json
+++ b/projects/common/assets/i18n/de.json
@@ -1,4 +1,11 @@
 {
   "pageIndication": "Seite {{index}}",
-  "close": "Schließen"
+  "close": "Schließen",
+  "validators": {
+    "inputRequired": "Eingabe erforderlich",
+    "inputRequiredTrue": "Ankreuzen erforderlich",
+    "inputTooShort": "Eingabe zu kurz",
+    "inputTooLong": "Eingabe zu lang",
+    "wrongPattern": "Eingabe enthält falsche Zeichen"
+  }
 }
diff --git a/projects/common/element-components/checkbox.component.ts b/projects/common/element-components/checkbox.component.ts
index ceae5c504..1c0b32fa9 100644
--- a/projects/common/element-components/checkbox.component.ts
+++ b/projects/common/element-components/checkbox.component.ts
@@ -1,25 +1,40 @@
 import { Component } from '@angular/core';
+import { ValidatorFn, Validators } from '@angular/forms';
 import { CheckboxElement } from '../unit';
 import { FormElementComponent } from '../form-element-component.directive';
 
 @Component({
   selector: 'app-checkbox',
   template: `
-    <mat-checkbox #checkbox class="example-margin"
-                  [formControl]="elementFormControl"
-                  [style.width.%]="100"
-                  [style.height.%]="100"
-                  [style.background-color]="elementModel.backgroundColor"
-                  [style.color]="elementModel.fontColor"
-                  [style.font-family]="elementModel.font"
-                  [style.font-size.px]="elementModel.fontSize"
-                  [style.font-weight]="elementModel.bold ? 'bold' : ''"
-                  [style.font-style]="elementModel.italic ? 'italic' : ''"
-                  [style.text-decoration]="elementModel.underline ? 'underline' : ''">
-      <div [innerHTML]="elementModel.label"></div>
-    </mat-checkbox>
+    <div class="mat-form-field">
+      <mat-checkbox #checkbox class="example-margin"
+                    [formControl]="elementFormControl"
+                    [style.width.%]="100"
+                    [style.height.%]="100"
+                    [style.background-color]="elementModel.backgroundColor"
+                    [style.color]="elementModel.fontColor"
+                    [style.font-family]="elementModel.font"
+                    [style.font-size.px]="elementModel.fontSize"
+                    [style.font-weight]="elementModel.bold ? 'bold' : ''"
+                    [style.font-style]="elementModel.italic ? 'italic' : ''"
+                    [style.text-decoration]="elementModel.underline ? 'underline' : ''">
+        <div [innerHTML]="elementModel.label"></div>
+      </mat-checkbox>
+      <mat-error *ngIf="elementFormControl.errors && elementFormControl.touched"
+                 [style.font-size.%]="75">
+        {{elementFormControl.errors | errorTransform: elementModel}}
+      </mat-error>
+    </div>
   `
 })
 export class CheckboxComponent extends FormElementComponent {
   elementModel!: CheckboxElement;
+
+  get validators(): ValidatorFn[] {
+    const validators: ValidatorFn[] = [];
+    if (this.elementModel.required) {
+      validators.push(Validators.requiredTrue);
+    }
+    return validators;
+  }
 }
diff --git a/projects/common/element-components/compound-components/correction.component.ts b/projects/common/element-components/compound-components/correction.component.ts
index 743d76323..459f5b025 100644
--- a/projects/common/element-components/compound-components/correction.component.ts
+++ b/projects/common/element-components/compound-components/correction.component.ts
@@ -1,4 +1,5 @@
 import { Component } from '@angular/core';
+import { ValidatorFn, Validators } from '@angular/forms';
 import { CompoundElementCorrection } from '../../unit';
 import { FormElementComponent } from '../../form-element-component.directive';
 
@@ -32,4 +33,12 @@ import { FormElementComponent } from '../../form-element-component.directive';
 })
 export class CorrectionComponent extends FormElementComponent {
   elementModel!: CompoundElementCorrection;
+
+  get validators(): ValidatorFn[] {
+    const validators: ValidatorFn[] = [];
+    if (this.elementModel.required) {
+      validators.push(Validators.required);
+    }
+    return validators;
+  }
 }
diff --git a/projects/common/element-components/dropdown.component.ts b/projects/common/element-components/dropdown.component.ts
index 0a7e2127e..50a0cfecb 100644
--- a/projects/common/element-components/dropdown.component.ts
+++ b/projects/common/element-components/dropdown.component.ts
@@ -1,31 +1,43 @@
 import { Component } from '@angular/core';
+import { ValidatorFn, Validators } from '@angular/forms';
 import { DropdownElement } from '../unit';
 import { FormElementComponent } from '../form-element-component.directive';
 
 @Component({
   selector: 'app-dropdown',
   template: `
-      <mat-form-field appearance="fill"
-                      [style.width.%]="100"
-                      [style.height.%]="100"
-                      [style.background-color]="elementModel.backgroundColor">
-          <mat-label [style.color]="elementModel.fontColor"
-                     [style.font-family]="elementModel.font"
-                     [style.font-size.px]="elementModel.fontSize"
-                     [style.font-weight]="elementModel.bold ? 'bold' : ''"
-                     [style.font-style]="elementModel.italic ? 'italic' : ''"
-                     [style.text-decoration]="elementModel.underline ? 'underline' : ''">
-              {{$any(elementModel).label}}
-          </mat-label>
-          <mat-select [formControl]="elementFormControl">
-              <mat-option *ngIf="elementModel.allowUnset" value=""></mat-option>
-              <mat-option *ngFor="let option of elementModel.options" [value]="option">
-                  {{option}}
-              </mat-option>
-          </mat-select>
-      </mat-form-field>
+    <mat-form-field appearance="fill"
+                    [style.width.%]="100"
+                    [style.height.%]="100"
+                    appInputBackgroundColor [backgroundColor]="elementModel.backgroundColor">
+      <mat-label [style.color]="elementModel.fontColor"
+                 [style.font-family]="elementModel.font"
+                 [style.font-size.px]="elementModel.fontSize"
+                 [style.font-weight]="elementModel.bold ? 'bold' : ''"
+                 [style.font-style]="elementModel.italic ? 'italic' : ''"
+                 [style.text-decoration]="elementModel.underline ? 'underline' : ''">
+        {{$any(elementModel).label}}
+      </mat-label>
+      <mat-select [formControl]="elementFormControl">
+        <mat-option *ngIf="elementModel.allowUnset" value=""></mat-option>
+        <mat-option *ngFor="let option of elementModel.options" [value]="option">
+          {{option}}
+        </mat-option>
+      </mat-select>
+      <mat-error *ngIf="elementFormControl.errors">
+        {{elementFormControl.errors | errorTransform: elementModel}}
+      </mat-error>
+    </mat-form-field>
   `
 })
 export class DropdownComponent extends FormElementComponent {
   elementModel!: DropdownElement;
+
+  get validators(): ValidatorFn[] {
+    const validators: ValidatorFn[] = [];
+    if (this.elementModel.required) {
+      validators.push(Validators.required);
+    }
+    return validators;
+  }
 }
diff --git a/projects/common/element-components/pipes/error-transform.pipe.ts b/projects/common/element-components/pipes/error-transform.pipe.ts
new file mode 100644
index 000000000..14bb4d290
--- /dev/null
+++ b/projects/common/element-components/pipes/error-transform.pipe.ts
@@ -0,0 +1,46 @@
+import { Pipe, PipeTransform } from '@angular/core';
+import { ValidationErrors } from '@angular/forms';
+import { TranslateService } from '@ngx-translate/core';
+import {
+  CheckboxElement, InputUIElement, TextFieldElement, UnitUIElement
+} from '../../unit';
+
+@Pipe({
+  name: 'errorTransform'
+})
+export class ErrorTransformPipe implements PipeTransform {
+  constructor(private translateService: TranslateService) {}
+
+  transform(validationErrors: ValidationErrors, elementModel: UnitUIElement): string {
+    const validationMessages = this.getValidationMessages(elementModel);
+    let returnMessage = '';
+
+    Object.keys(validationErrors).forEach(errorKey => {
+      if (returnMessage) {
+        returnMessage += '; ';
+      }
+      const messageKey = errorKey === 'required' && elementModel.type === 'checkbox' ? 'requiredTrue' : errorKey;
+      returnMessage += validationMessages[messageKey];
+    });
+    return returnMessage;
+  }
+
+  private getValidationMessages(elementModel: UnitUIElement): Record<string, string> {
+    return {
+      required: (elementModel as InputUIElement).requiredWarnMessage ||
+        this.translateService.instant('validators.inputRequired'),
+
+      requiredTrue: (elementModel as CheckboxElement).requiredWarnMessage ||
+        this.translateService.instant('validators.inputRequiredTrue'),
+
+      minlength: (elementModel as TextFieldElement).minWarnMessage ||
+        this.translateService.instant('validators.inputTooShort'),
+
+      maxlength: (elementModel as TextFieldElement).maxWarnMessage ||
+        this.translateService.instant('validators.inputTooLong'),
+
+      pattern: (elementModel as TextFieldElement).patternWarnMessage ||
+        this.translateService.instant('validators.wrongPattern')
+    };
+  }
+}
diff --git a/projects/common/pipes/safe-resource-url.pipe.ts b/projects/common/element-components/pipes/safe-resource-url.pipe.ts
similarity index 100%
rename from projects/common/pipes/safe-resource-url.pipe.ts
rename to projects/common/element-components/pipes/safe-resource-url.pipe.ts
diff --git a/projects/common/element-components/radio-button-group.component.ts b/projects/common/element-components/radio-button-group.component.ts
index 9531807c2..01341c0d6 100644
--- a/projects/common/element-components/radio-button-group.component.ts
+++ b/projects/common/element-components/radio-button-group.component.ts
@@ -1,11 +1,13 @@
 import { Component } from '@angular/core';
+import { ValidatorFn, Validators } from '@angular/forms';
 import { RadioButtonGroupElement } from '../unit';
 import { FormElementComponent } from '../form-element-component.directive';
 
 @Component({
   selector: 'app-radio-button-group',
   template: `
-    <div [style.width.%]="100"
+    <div class="mat-form-field"
+         [style.width.%]="100"
          [style.height.%]="100"
          [style.background-color]="elementModel.backgroundColor"
          [style.color]="elementModel.fontColor"
@@ -22,10 +24,22 @@ import { FormElementComponent } from '../form-element-component.directive';
         <mat-radio-button *ngFor="let option of elementModel.options" [value]="option">
           {{option}}
         </mat-radio-button>
+        <mat-error *ngIf="elementFormControl.errors && elementFormControl.touched"
+                   [style.font-size.%]="75">
+          {{elementFormControl.errors | errorTransform: elementModel}}
+        </mat-error>
       </mat-radio-group>
     </div>
   `
 })
 export class RadioButtonGroupComponent extends FormElementComponent {
   elementModel!: RadioButtonGroupElement;
+
+  get validators(): ValidatorFn[] {
+    const validators: ValidatorFn[] = [];
+    if (this.elementModel.required) {
+      validators.push(Validators.required);
+    }
+    return validators;
+  }
 }
diff --git a/projects/common/element-components/text-area.component.ts b/projects/common/element-components/text-area.component.ts
index feb6bb231..f2360c6b3 100644
--- a/projects/common/element-components/text-area.component.ts
+++ b/projects/common/element-components/text-area.component.ts
@@ -1,4 +1,5 @@
 import { Component, Output, EventEmitter } from '@angular/core';
+import { ValidatorFn, Validators } from '@angular/forms';
 import { TextAreaElement } from '../unit';
 import { FormElementComponent } from '../form-element-component.directive';
 
@@ -15,13 +16,16 @@ import { FormElementComponent } from '../form-element-component.directive';
                     [style.font-style]="elementModel.italic ? 'italic' : ''"
                     [style.text-decoration]="elementModel.underline ? 'underline' : ''"
                     [appearance]="$any(elementModel.appearance)">
-    <textarea matInput [formControl]="elementFormControl" #input
-              (focus)="onFocus.emit(input)"
-              (blur)="onBlur.emit(input)"
-              placeholder="{{elementModel.label}}"
-              [style.min-width.%]="100"
-              [style.resize]="elementModel.resizeEnabled ? 'both' : 'none'">
-    </textarea>
+      <textarea matInput [formControl]="elementFormControl" #input
+                (focus)="onFocus.emit(input)"
+                (blur)="onBlur.emit(input)"
+                placeholder="{{elementModel.label}}"
+                [style.min-width.%]="100"
+                [style.resize]="elementModel.resizeEnabled ? 'both' : 'none'">
+      </textarea>
+      <mat-error *ngIf="elementFormControl.errors">
+        {{elementFormControl.errors | errorTransform: elementModel}}
+      </mat-error>
     </mat-form-field>
   `
 })
@@ -29,4 +33,12 @@ export class TextAreaComponent extends FormElementComponent {
   @Output() onFocus = new EventEmitter<HTMLElement>();
   @Output() onBlur = new EventEmitter<HTMLElement>();
   elementModel!: TextAreaElement;
+
+  get validators(): ValidatorFn[] {
+    const validators: ValidatorFn[] = [];
+    if (this.elementModel.required) {
+      validators.push(Validators.required);
+    }
+    return validators;
+  }
 }
diff --git a/projects/common/element-components/text-field.component.ts b/projects/common/element-components/text-field.component.ts
index 9cc669da3..e2c95bf6e 100644
--- a/projects/common/element-components/text-field.component.ts
+++ b/projects/common/element-components/text-field.component.ts
@@ -1,4 +1,5 @@
 import { Component, EventEmitter, Output } from '@angular/core';
+import { ValidatorFn, Validators } from '@angular/forms';
 import { TextFieldElement } from '../unit';
 import { FormElementComponent } from '../form-element-component.directive';
 
@@ -20,6 +21,9 @@ import { FormElementComponent } from '../form-element-component.directive';
              (blur)="onBlur.emit(input)"
              [formControl]="elementFormControl"
              placeholder="{{elementModel.label}}">
+      <mat-error *ngIf="elementFormControl.errors">
+        {{elementFormControl.errors | errorTransform: elementModel}}
+      </mat-error>
     </mat-form-field>
   `
 })
@@ -27,4 +31,21 @@ export class TextFieldComponent extends FormElementComponent {
   @Output() onFocus = new EventEmitter<HTMLElement>();
   @Output() onBlur = new EventEmitter<HTMLElement>();
   elementModel!: TextFieldElement;
+
+  get validators(): ValidatorFn[] {
+    const validators: ValidatorFn[] = [];
+    if (this.elementModel.required) {
+      validators.push(Validators.required);
+    }
+    if (this.elementModel.minLength) {
+      validators.push(Validators.minLength(<number> this.elementModel.minLength));
+    }
+    if (this.elementModel.maxLength) {
+      validators.push(Validators.maxLength(<number> this.elementModel.maxLength));
+    }
+    if (this.elementModel.pattern) {
+      validators.push(Validators.pattern(<string> this.elementModel.pattern));
+    }
+    return validators;
+  }
 }
diff --git a/projects/common/form-element-component.directive.ts b/projects/common/form-element-component.directive.ts
index 51318d0e3..f3933d831 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
+  FormControl, FormGroup, ValidatorFn
 } from '@angular/forms';
 import { Subject } from 'rxjs';
 import { pairwise, startWith, takeUntil } from 'rxjs/operators';
@@ -17,6 +17,8 @@ export abstract class FormElementComponent extends ElementComponent implements O
   parentForm!: FormGroup;
   defaultValue!: string | number | boolean | undefined;
   elementFormControl!: FormControl;
+  abstract validators: ValidatorFn[];
+
   private ngUnsubscribe = new Subject<void>();
 
   constructor(private formService: FormService) {
@@ -31,6 +33,11 @@ export abstract class FormElementComponent extends ElementComponent implements O
     });
     this.elementFormControl = this.formControl;
     this.updateFormValue((this.elementModel as InputUIElement).value);
+    this.formService.setValidators({
+      id: this.elementModel.id,
+      validators: this.validators,
+      formGroup: this.parentForm
+    });
     this.elementFormControl.valueChanges
       .pipe(
         startWith(this.elementModel.value),
diff --git a/projects/common/form.service.ts b/projects/common/form.service.ts
index d84d85844..190adfa66 100644
--- a/projects/common/form.service.ts
+++ b/projects/common/form.service.ts
@@ -37,8 +37,8 @@ export class FormService {
     this._groupAdded.next(group);
   }
 
-  setValidators(validations: FormControlValidators): void {
-    this._validatorsAdded.next(validations);
+  setValidators(validators: FormControlValidators): void {
+    this._validatorsAdded.next(validators);
   }
 
   addPresentedPage(presentedPage: number): void {
diff --git a/projects/player/src/app/app.module.ts b/projects/player/src/app/app.module.ts
index e6f583b0b..56a6f89b8 100644
--- a/projects/player/src/app/app.module.ts
+++ b/projects/player/src/app/app.module.ts
@@ -9,7 +9,6 @@ import { PageComponent } from './components/page/page.component';
 import { SectionComponent } from './components/section/section.component';
 import { SharedModule } from '../../../common/app.module';
 import { ElementOverlayComponent } from './components/element-overlay/element-overlay.component';
-import { ValidationMessageComponent } from './components/validation-message/validation-message.component';
 import { UnitStateComponent } from './components/unit-state/unit-state.component';
 import { PlayerStateComponent } from './components/player-state/player-state.component';
 import { PlayerTranslateLoader } from './classes/player-translate-loader';
@@ -28,7 +27,6 @@ import { ElementComponent } from './components/element/element.component';
     PageComponent,
     SectionComponent,
     ElementOverlayComponent,
-    ValidationMessageComponent,
     UnitStateComponent,
     PlayerStateComponent,
     LayoutComponent,
diff --git a/projects/player/src/app/components/element/element.component.html b/projects/player/src/app/components/element/element.component.html
index 3970878d1..56a216192 100644
--- a/projects/player/src/app/components/element/element.component.html
+++ b/projects/player/src/app/components/element/element.component.html
@@ -22,8 +22,4 @@
   </app-element-overlay>
 </ng-template>
 
-<app-validation-message *ngIf="isInputElement"
-                        [ngClass]="{'dynamic-validation-messages' : elementModel.dynamicPositioning}"
-                        [parentForm]="elementForm"
-                        [elementModel]="elementModel">
-</app-validation-message>
+
diff --git a/projects/player/src/app/components/validation-message/validation-message.component.css b/projects/player/src/app/components/validation-message/validation-message.component.css
deleted file mode 100644
index 8352262b6..000000000
--- a/projects/player/src/app/components/validation-message/validation-message.component.css
+++ /dev/null
@@ -1,9 +0,0 @@
-.dynamic-validation-message{
-  display: contents;
-}
-
-.dynamic-validation-message:not(:empty):not(:last-child):after {
-  content: ", ";
-}
-
-
diff --git a/projects/player/src/app/components/validation-message/validation-message.component.html b/projects/player/src/app/components/validation-message/validation-message.component.html
deleted file mode 100644
index d953e911a..000000000
--- a/projects/player/src/app/components/validation-message/validation-message.component.html
+++ /dev/null
@@ -1,22 +0,0 @@
-<div *ngIf="formElementControl?.touched" class="mat-typography">
-  <mat-error *ngIf="formElementControl.errors?.required && elementModel.type !== 'checkbox'"
-             [ngClass]="{'dynamic-validation-message' : elementModel.dynamicPositioning}" >
-    {{requiredMessage}}
-  </mat-error>
-  <mat-error *ngIf="formElementControl.errors?.required && elementModel.type === 'checkbox'"
-             [ngClass]="{'dynamic-validation-message' : elementModel.dynamicPositioning}" >
-    {{requiredTrueMessage}}
-  </mat-error>
-  <mat-error *ngIf="formElementControl.errors?.minlength"
-             [ngClass]="{'dynamic-validation-message' : elementModel.dynamicPositioning}" >
-    {{minLengthWarnMessage}}
-  </mat-error>
-  <mat-error *ngIf="formElementControl.errors?.maxlength"
-             [ngClass]="{'dynamic-validation-message' : elementModel.dynamicPositioning}" >
-    {{maxLengthWarnMessage}}
-  </mat-error>
-  <mat-error *ngIf="formElementControl.errors?.pattern"
-             [ngClass]="{'dynamic-validation-message' : elementModel.dynamicPositioning}" >
-    {{patternMessage}}
-  </mat-error>
-</div>
diff --git a/projects/player/src/app/components/validation-message/validation-message.component.ts b/projects/player/src/app/components/validation-message/validation-message.component.ts
deleted file mode 100644
index 02e7337f1..000000000
--- a/projects/player/src/app/components/validation-message/validation-message.component.ts
+++ /dev/null
@@ -1,80 +0,0 @@
-import {
-  Component, Input, OnInit
-} from '@angular/core';
-import {
-  FormControl, FormGroup, ValidatorFn, Validators
-} from '@angular/forms';
-import { TranslateService } from '@ngx-translate/core';
-import {
-  CheckboxElement,
-  InputUIElement, TextFieldElement, UnitUIElement
-} from '../../../../../common/unit';
-import { FormService } from '../../../../../common/form.service';
-
-@Component({
-  selector: 'app-validation-message',
-  templateUrl: './validation-message.component.html',
-  styleUrls: ['./validation-message.component.css']
-})
-
-export class ValidationMessageComponent implements OnInit {
-  @Input() elementModel!: UnitUIElement;
-  @Input() parentForm!: FormGroup;
-  formElementControl!: FormControl;
-  requiredMessage!: string;
-  requiredTrueMessage!: string;
-  minLengthWarnMessage!: string;
-  maxLengthWarnMessage!: string;
-  patternMessage!: string;
-
-  constructor(private formService: FormService,
-              private translateService: TranslateService) {}
-
-  ngOnInit(): void {
-    this.setErrorMessages();
-    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) {
-      if (this.elementModel.type === 'checkbox') {
-        validators.push(Validators.requiredTrue);
-      } else {
-        validators.push(Validators.required);
-      }
-    }
-    if (this.elementModel.minLength) {
-      validators.push(Validators.minLength(<number> this.elementModel.minLength));
-    }
-    if (this.elementModel.maxLength) {
-      validators.push(Validators.maxLength(<number> this.elementModel.maxLength));
-    }
-    if (this.elementModel.pattern) {
-      validators.push(Validators.pattern(<string> this.elementModel.pattern));
-    }
-    return validators;
-  }
-
-  private setErrorMessages() {
-    this.requiredMessage = (this.elementModel as InputUIElement).requiredWarnMessage ||
-      this.translateService.instant('validators.inputRequired');
-
-    this.requiredTrueMessage = (this.elementModel as CheckboxElement).requiredWarnMessage ||
-      this.translateService.instant('validators.inputRequiredTrue');
-
-    this.minLengthWarnMessage = (this.elementModel as TextFieldElement).minWarnMessage ||
-      this.translateService.instant('validators.inputTooShort');
-
-    this.maxLengthWarnMessage = (this.elementModel as TextFieldElement).maxWarnMessage ||
-      this.translateService.instant('validators.inputTooLong');
-
-    this.patternMessage = (this.elementModel as TextFieldElement).patternWarnMessage ||
-      this.translateService.instant('validators.wrongPattern');
-  }
-}
diff --git a/projects/player/src/assets/i18n/de.json b/projects/player/src/assets/i18n/de.json
index 049ea0b40..0373373de 100644
--- a/projects/player/src/assets/i18n/de.json
+++ b/projects/player/src/assets/i18n/de.json
@@ -3,13 +3,6 @@
   "presentationIncomplete": "Bearbeitung unvollständig",
   "responsesIncomplete": "Antworten sind unvollständig",
   "noReason": "Navigation ohne Angabe von Gründen verweigert",
-  "validators": {
-    "inputRequired": "Eingabe erforderlich",
-    "inputRequiredTrue": "Ankreuzen erforderlich",
-    "inputTooShort": "Eingabe zu kurz",
-    "inputTooLong": "Eingabe zu lang",
-    "wrongPattern": "Eingabe enthält falsche Zeichen"
-  },
   "dialogTitle": {
     "wrongUnitDefinitionType": "Falscher Unit-Definition-Type",
     "wrongUnitStateDataType": "Falscher Unit-State-Data-Type"
-- 
GitLab