nm-dynamic-form-fields-component
    src/app/shared/widgets/dynamic-form/dynamic-form-component/dynamic-form-fields.component.ts
| changeDetection | ChangeDetectionStrategy.OnPush | 
| selector | nm-dynamic-form-fields-component | 
| styleUrls | dynamic-form-fields.component.scss | 
| templateUrl | ./dynamic-form-fields.component.html | 
| constructor(cdr: ChangeDetectorRef, widgetFrameService: WidgetframeService, currentLocaleService: CurrentLocaleService, dateAdapter: DateAdapter | |||||||||||||||||||||||||||||||||
| 
                                    Parameters :
                                     
 | 
| addFields | Adds fields dynamically to the form. | 
| configuration | Sets dynamic form configurations. 
                            Type:     | 
| dialogContext | Sets dialog context for field used with page-dialog type fields. | 
| fields | Sets dynamic form fields. 
                            Type:     | 
| labelSetter | Sets field label. | 
| lookupOptions | Sets lookup options for lookup types. | 
| reloadLookup | Reloads field lookup options. | 
| removeAllFields | Removes all fields dynamically from the form. | 
| removeFields | Removes fields dynamically from the form. | 
| reset | Resets all form fields. | 
| resetFormFields | Resets all form fields. | 
| resetLookup | Resets field lookup options. | 
| setDisabled | Enables\Disables field. | 
| setHidden | Shows\Hides field. | 
| triggerValidation | Triggers validation for form fields. | 
| valueSetter | Sets field value. | 
| droppedValue | Emits the dropped value when using drag and drop featuer. $eventtype:EventEmitter | 
| init | Emits when initialization is finished for all fields and emits also before form destroy. $eventtype:EventEmitter | 
| onClick | Emits when any field is clicked. $eventtype:EventEmitter | 
| onEnterPressed | Emits when enter is pressed. $eventtype:EventEmitter | 
| valid | Emits validation result when any field value changed. $eventtype:EventEmitter | 
| value | Emits when any field value changed. $eventtype:EventEmitter | 
| addChip | |||||||||
| addChip(event: MatChipInputEvent, field: DynamicFormField) | |||||||||
| 
                                    Parameters :
                                     
 
                                Returns :      void | 
| Public booleanValueChanged | ||||||
| booleanValueChanged(field: DynamicFormField) | ||||||
| 
                                    Parameters :
                                     
 
                                Returns :      void | 
| Public changePassword | ||||
| changePassword(field: ) | ||||
| 
                                    Parameters :
                                     
 
                                Returns :      void | 
| Public clearField | ||||||
| clearField(field: DynamicFormField) | ||||||
| 
                                    Parameters :
                                     
 
                                Returns :      void | 
| Private findField | ||||||
| findField(identifier: string) | ||||||
| 
                                    Parameters :
                                     
 
                                Returns :      any | 
| Public getButtonActions | ||||||
| getButtonActions(field: DynamicFormField) | ||||||
| 
                                    Parameters :
                                     
 
                                Returns :      Observable<Content[]> | 
| Public getCategoryUrl | ||||||
| getCategoryUrl(field: DynamicFormField) | ||||||
| 
                                    Parameters :
                                     
 
                                Returns :      any | 
| Public getClass | ||||||
| getClass(field: DynamicFormField) | ||||||
| 
                                    Parameters :
                                     
 
                                Returns :      any | 
| Public getDescriptionField | ||||||
| getDescriptionField(field: DynamicFormField) | ||||||
| 
                                    Parameters :
                                     
 
                                Returns :      string | 
| Public getIdentifierField | ||||||
| getIdentifierField(field: DynamicFormField) | ||||||
| 
                                    Parameters :
                                     
 
                                Returns :      string | 
| Public getInteractions | ||||||
| getInteractions(field: DynamicFormField) | ||||||
| 
                                    Parameters :
                                     
 
                                Returns :      literal type | null | 
| Public getLocalizedValue | ||||||
| getLocalizedValue(field: DynamicFormField) | ||||||
| 
                                    Parameters :
                                     
 
                                Returns :      string | 
| Public getMenuActions | ||||||
| getMenuActions(field: DynamicFormField) | ||||||
| 
                                    Parameters :
                                     
 
                                Returns :      Observable<Content[]> | 
| getPreferredPosition | ||||||
| getPreferredPosition(field: DynamicFormField) | ||||||
| 
                                    Parameters :
                                     
 
                                Returns :      any | 
| handlePaste | ||||||
| handlePaste(event: ClipboardEvent) | ||||||
| 
                                    Parameters :
                                     
 
                                Returns :      void | 
| Public includeSubCategoriesValueChanged | |||||||||
| includeSubCategoriesValueChanged(field: DynamicFormField, value: any) | |||||||||
| 
                                    Parameters :
                                     
 
                                Returns :      void | 
| Private initFields | ||||||
| initFields(fields: any[]) | ||||||
| 
                                    Parameters :
                                     
 
                                Returns :      void | 
| Private initializeButtonActions | ||||||
| initializeButtonActions(field: DynamicFormField) | ||||||
| 
                                    Parameters :
                                     
 
                                Returns :      void | 
| Private initializeMenuActions | ||||||
| initializeMenuActions(field: DynamicFormField) | ||||||
| 
                                    Parameters :
                                     
 
                                Returns :      void | 
| Public loadLookupData | ||||||
| loadLookupData(field: DynamicFormField) | ||||||
| 
                                    Parameters :
                                     
 
                                Returns :      void | 
| ngOnDestroy | 
| ngOnDestroy() | 
| 
                                Returns :      void | 
| Public ngOnInit | 
| ngOnInit() | 
| 
                                Returns :      void | 
| onClick | |||||||||
| onClick(event: , field: DynamicFormField) | |||||||||
| 
                                    Parameters :
                                     
 
                                Returns :      void | 
| onEditorValueChange | |||||||||
| onEditorValueChange(field: DynamicFormField, value: any) | |||||||||
| 
                                    Parameters :
                                     
 
                                Returns :      void | 
| onEnterPressed | ||||
| onEnterPressed(event: ) | ||||
| 
                                    Parameters :
                                     
 
                                Returns :      void | 
| Public onExternalModelChange | |||||||||
| onExternalModelChange(field: DynamicFormField, value: any) | |||||||||
| 
                                    Parameters :
                                     
 
                                Returns :      void | 
| Public onFileUpload | |||||||||
| onFileUpload(field: , uploadedFile: any) | |||||||||
| 
                                    Parameters :
                                     
 
                                Returns :      void | 
| onFocusIn | ||||||
| onFocusIn(field: DynamicFormField) | ||||||
| 
                                    Parameters :
                                     
 
                                Returns :      void | 
| onFocusOut | ||||||
| onFocusOut(field: DynamicFormField) | ||||||
| 
                                    Parameters :
                                     
 
                                Returns :      void | 
| Public onItemDropped | ||||||
| onItemDropped(ev: , field: ) | ||||||
| 
                                    Parameters :
                                     
 
                                Returns :      void | 
| onKeyPressed | |||||||||
| onKeyPressed(event: KeyboardEvent, field: ) | |||||||||
| 
                                    Parameters :
                                     
 
                                Returns :      boolean | 
| onKeyPressedDatePicker | ||||
| onKeyPressedDatePicker(event: ) | ||||
| 
                                    Parameters :
                                     
 
                                Returns :      void | 
| onKeyPressedDateTimePicker | ||||
| onKeyPressedDateTimePicker(event: ) | ||||
| 
                                    Parameters :
                                     
 
                                Returns :      void | 
| onTriggerValidation | 
| onTriggerValidation() | 
| 
                                Returns :      void | 
| onValueChange | ||||||||||||
| onValueChange(field: DynamicFormField, isEdit: boolean) | ||||||||||||
| 
                                    Parameters :
                                     
 
                                Returns :      void | 
| Public openPageDialog | ||||||
| openPageDialog(field: DynamicFormField) | ||||||
| 
                                    Parameters :
                                     
 
                                Returns :      void | 
| Public openSelectDialog | ||||||
| openSelectDialog(field: DynamicFormField) | ||||||
| 
                                    Parameters :
                                     
 
                                Returns :      void | 
| Public openUserRightDialog | ||||||
| openUserRightDialog(field: DynamicFormField) | ||||||
| 
                                    Parameters :
                                     
 
                                Returns :      void | 
| Public openWorklistDialog | ||||||
| openWorklistDialog(field: DynamicFormField) | ||||||
| 
                                    Parameters :
                                     
 
                                Returns :      void | 
| Private pageDialog | ||||||||||||||||||
| pageDialog(field: DynamicFormField, module: string, identifier: string, context: any, minWidth: string) | ||||||||||||||||||
| 
                                    Parameters :
                                     
 
                                Returns :      void | 
| removeChip | |||||||||
| removeChip(chip: any, field: DynamicFormField) | |||||||||
| 
                                    Parameters :
                                     
 
                                Returns :      void | 
| resetDateTime | ||||||
| resetDateTime(field: , event: ) | ||||||
| 
                                    Parameters :
                                     
 
                                Returns :      void | 
| Private setFieldDefaultValue | ||||||
| setFieldDefaultValue(field: DynamicFormField) | ||||||
| 
                                    Parameters :
                                     
 
                                Returns :      void | 
| Private setFieldDescription | |||||||||
| setFieldDescription(field: DynamicFormField, value: any) | |||||||||
| 
                                    Parameters :
                                     
 
                                Returns :      void | 
| Private setLookupOptions | ||||||
| setLookupOptions(field: , value: ) | ||||||
| 
                                    Parameters :
                                     
 
                                Returns :      void | 
| Private updateLocale | |||||||||
| updateLocale(locale: string, fields: DynamicFormField[]) | |||||||||
| 
                                    Parameters :
                                     
 
                                Returns :      void | 
| Private validateCategorySelector | ||||||
| validateCategorySelector(field: DynamicFormField) | ||||||
| 
                                    Parameters :
                                     
 
                                Returns :      boolean | 
| Private validateFields | 
| validateFields() | 
| 
                                Returns :      boolean | 
| Private validateLocalizedText | ||||||
| validateLocalizedText(field: DynamicFormField) | ||||||
| 
                                    Parameters :
                                     
 
                                Returns :      boolean | 
| Private validatePasswords | ||||||
| validatePasswords(field: DynamicFormField) | ||||||
| 
                                    Parameters :
                                     
 
                                Returns :      boolean | 
| Private validateSourceCodeEditor | ||||
| validateSourceCodeEditor(field: ) | ||||
| 
                                    Parameters :
                                     
 
                                Returns :      any | 
| Public addFieldsChannel | 
| addFieldsChannel:      | 
| Default value : new Subject<DynamicFormField[]>() | 
| Public buttonActions | 
| buttonActions:      | 
| Type : literal type | 
| Default value : {} | 
| Public currentLocale | 
| currentLocale:      | 
| Type : any | 
| datePicker | 
| datePicker:      | 
| Type : OwlDateTimeComponent<Date> | 
| Decorators : ViewChild | 
| dateTimePicker | 
| dateTimePicker:      | 
| Type : OwlDateTimeComponent<any> | 
| Decorators : ViewChild | 
| Public description | 
| description:      | 
| Type : object | 
| Default value : {} | 
| Public dialogContextChannel | 
| dialogContextChannel:      | 
| Default value : new Subject<{ field: string; context: any }>() | 
| Public dialogSelectedItems | 
| dialogSelectedItems:      | 
| Type : object | 
| Default value : {} | 
| Public editorConfig | 
| editorConfig:      | 
| Type : any | 
| Default value : DEFAULT_TEXT_EDITOR_CONFIG | 
| Public labelSetterChannel | 
| labelSetterChannel:      | 
| Default value : new Subject<{ field: string; value: any }>() | 
| Public lookupOptions | 
| lookupOptions:      | 
| Type : object | 
| Default value : {} | 
| Public lookupOptionsInputChannel | 
| lookupOptionsInputChannel:      | 
| Default value : new Subject<any>() | 
| Public menuActions | 
| menuActions:      | 
| Type : literal type | 
| Default value : {} | 
| Public passwordConfirmation | 
| passwordConfirmation:      | 
| Type : object | 
| Default value : {} | 
| Public reloadLookupChannel | 
| reloadLookupChannel:      | 
| Default value : new Subject<string>() | 
| Public removeAllFieldsChannel | 
| removeAllFieldsChannel:      | 
| Default value : new Subject<any>() | 
| Public removeFieldsChannel | 
| removeFieldsChannel:      | 
| Default value : new Subject<string[]>() | 
| Public resetChannel | 
| resetChannel:      | 
| Default value : new Subject<any>() | 
| Public resetDateValue | 
| resetDateValue:      | 
| Default value : new Subject<any>() | 
| Public resetFormFieldsChannel | 
| resetFormFieldsChannel:      | 
| Default value : new Subject<any>() | 
| Public resetLookupChannel | 
| resetLookupChannel:      | 
| Default value : new Subject<string>() | 
| Public separatorKeysCodes | 
| separatorKeysCodes:      | 
| Type : number[] | 
| Default value : [ENTER, COMMA] | 
| Public setDisabledChannel | 
| setDisabledChannel:      | 
| Default value : new Subject<{ field: string; value: boolean }>() | 
| Public setHiddenChannel | 
| setHiddenChannel:      | 
| Default value : new Subject<{ field: string; value: boolean }>() | 
| Public showPassword | 
| showPassword:      | 
| Type : boolean | 
| Default value : false | 
| Public sourceCodeValidations | 
| sourceCodeValidations:      | 
| Type : object | 
| Default value : {} | 
| Public triggerValidationChannel | 
| triggerValidationChannel:      | 
| Default value : new Subject() | 
| Public unsubscribe | 
| unsubscribe:      | 
| Default value : NgUnsubscribe.create() | 
| Public value | 
| value:      | 
| Type : object | 
| Default value : {} | 
| Public valueSetterChannel | 
| valueSetterChannel:      | 
| Default value : new Subject<{ field: string; value: any }>() | 
| lookupOptionsInput | ||||
| set lookupOptionsInput(value: ) | ||||
| Sets lookup options for lookup types. 
                                        Parameters :
                                         
 
                                    Returns :      void | 
| resetLookup | ||||
| set resetLookup(value: ) | ||||
| Resets field lookup options. 
                                        Parameters :
                                         
 
                                    Returns :      void | 
| valueSetter | ||||
| set valueSetter(value: ) | ||||
| Sets field value. 
                                        Parameters :
                                         
 
                                    Returns :      void | 
| labelSetter | ||||
| set labelSetter(value: ) | ||||
| Sets field label. 
                                        Parameters :
                                         
 
                                    Returns :      void | 
| setDisabled | ||||
| set setDisabled(value: ) | ||||
| Enables\Disables field. 
                                        Parameters :
                                         
 
                                    Returns :      void | 
| dialogContext | ||||
| set dialogContext(value: ) | ||||
| Sets dialog context for field used with page-dialog type fields. 
                                        Parameters :
                                         
 
                                    Returns :      void | 
| resetFormFields | ||||
| set resetFormFields(value: ) | ||||
| Resets all form fields. 
                                        Parameters :
                                         
 
                                    Returns :      void | 
| setHidden | ||||
| set setHidden(value: ) | ||||
| Shows\Hides field. 
                                        Parameters :
                                         
 
                                    Returns :      void | 
| reset | ||||
| set reset(value: ) | ||||
| Resets all form fields. 
                                        Parameters :
                                         
 
                                    Returns :      void | 
| reloadLookup | ||||
| set reloadLookup(value: ) | ||||
| Reloads field lookup options. 
                                        Parameters :
                                         
 
                                    Returns :      void | 
| addFields | ||||
| set addFields(value: ) | ||||
| Adds fields dynamically to the form. 
                                        Parameters :
                                         
 
                                    Returns :      void | 
| removeFields | ||||
| set removeFields(value: ) | ||||
| Removes fields dynamically from the form. 
                                        Parameters :
                                         
 
                                    Returns :      void | 
| removeAllFields | ||||
| set removeAllFields(value: ) | ||||
| Removes all fields dynamically from the form. 
                                        Parameters :
                                         
 
                                    Returns :      void | 
| triggerValidation | ||||
| set triggerValidation(value: ) | ||||
| Triggers validation for form fields. 
                                        Parameters :
                                         
 
                                    Returns :      void | 
import {
  ChangeDetectionStrategy,
  ChangeDetectorRef,
  Component,
  OnDestroy,
  Input,
  Output,
  EventEmitter,
  ViewChild,
} from "@angular/core";
import { EMPTY, Observable, Subject } from "rxjs";
import { NgUnsubscribe } from "../../../ng-unsubscribe";
import { map, takeUntil, withLatestFrom } from "rxjs/operators";
import { WidgetframeService } from "../../widgetframe/widgetframe.service";
import { SelectItemsDialogComponent } from "../../../components/select-items-dialog/select-items-dialog.component";
import { CurrentLocaleService } from "../../../components/i18n/currentLocale.service";
import { DEFAULT_TEXT_EDITOR_CONFIG } from "../../../components/tiny-text-editor/tiny-text-editor.component";
import { BaseConfiguration } from "../../widgetframe/widgetframe.component";
import { DataValidation } from "../../../components/validation/validation.service";
import { ValidationError } from "../../../components/validation/validators";
import { TranslateService } from "@ngx-translate/core";
import { OwlDateTimeComponent, OwlDateTimeIntl } from "ng-pick-datetime";
import { DateAdapter } from "@angular/material/core";
import { getOrDefault } from "../../widget.configuration";
import {
  angularWidgetBridgeInput,
  stripPasteContent,
} from "../../../components/util/util.service";
import { PageDialogComponent } from "../../../components/dialog/page-dialog.component";
import { COMMA, ENTER } from "@angular/cdk/keycodes";
import { MatChipInputEvent } from "@angular/material/chips";
import { AppContext } from "../../../components/app-context/app.context";
import { Content, Selectors } from "../../../components/app-context/api";
import { filter } from "rxjs/operators";
import { ValidationService } from "../../../components/validation/validation.service";
import { DateTimeAdapter } from "ng-pick-datetime";
import { WidgetForPipe } from "../../../widgets/container/widget-for.pipe";
import { DialogService } from "../../../components/dialog";
declare var contextPath: string;
@Component({
  selector: "nm-dynamic-form-fields-component",
  templateUrl: "./dynamic-form-fields.component.html",
  styleUrls: ["./dynamic-form-fields.component.scss"],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class DynamicFormFieldsComponent implements OnDestroy {
  public unsubscribe = NgUnsubscribe.create();
  public editorConfig: any = DEFAULT_TEXT_EDITOR_CONFIG;
  public lookupOptions = {};
  public dialogSelectedItems = {};
  public value = {};
  public description = {};
  public passwordConfirmation = {};
  public sourceCodeValidations = {};
  public menuActions: { [key: string]: Observable<Content[]> } = {};
  public buttonActions: { [key: string]: Observable<Content[]> } = {};
  public resetDateValue = new Subject<any>();
  public showPassword: boolean = false;
  public separatorKeysCodes: number[] = [ENTER, COMMA];
  public currentLocale: any;
  /**
   * Sets dynamic form fields.
   */
  @Input()
  public fields: DynamicFormField[];
  /**
   * Sets dynamic form configurations.
   */
  @Input()
  public configuration: DynamicFormConfiguration;
  /**
   * Sets lookup options for lookup types.
   */
  @Input("lookupOptions")
  public set lookupOptionsInput(value) {
    angularWidgetBridgeInput(
      value,
      this.lookupOptionsInputChannel,
      this.unsubscribe
    );
  }
  /**
   * Resets field lookup options.
   */
  @Input("resetLookup")
  public set resetLookup(value) {
    angularWidgetBridgeInput(value, this.resetLookupChannel, this.unsubscribe);
  }
  /**
   * Sets field value.
   */
  @Input("valueSetter")
  public set valueSetter(value) {
    angularWidgetBridgeInput(value, this.valueSetterChannel, this.unsubscribe);
  }
  /**
   * Sets field label.
   */
  @Input("labelSetter")
  public set labelSetter(value) {
    angularWidgetBridgeInput(value, this.labelSetterChannel, this.unsubscribe);
  }
  /**
   * Enables\Disables field.
   */
  @Input("setDisabled")
  public set setDisabled(value) {
    angularWidgetBridgeInput(value, this.setDisabledChannel, this.unsubscribe);
  }
  /**
   * Sets dialog context for field used with page-dialog type fields.
   */
  @Input("dialogContext")
  public set dialogContext(value) {
    angularWidgetBridgeInput(
      value,
      this.dialogContextChannel,
      this.unsubscribe
    );
  }
  /**
   * Resets all form fields.
   */
  @Input("resetFormFields")
  public set resetFormFields(value) {
    angularWidgetBridgeInput(
      value,
      this.resetFormFieldsChannel,
      this.unsubscribe
    );
  }
  /**
   * Shows\Hides field.
   */
  @Input("setHidden")
  public set setHidden(value) {
    angularWidgetBridgeInput(value, this.setHiddenChannel, this.unsubscribe);
  }
  /**
   * Resets all form fields.
   */
  @Input("reset")
  public set reset(value) {
    angularWidgetBridgeInput(value, this.resetChannel, this.unsubscribe);
  }
  /**
   * Reloads field lookup options.
   */
  @Input("reloadLookup")
  public set reloadLookup(value) {
    angularWidgetBridgeInput(value, this.reloadLookupChannel, this.unsubscribe);
  }
  /**
   * Adds fields dynamically to the form.
   */
  @Input("addFields")
  public set addFields(value) {
    angularWidgetBridgeInput(value, this.addFieldsChannel, this.unsubscribe);
  }
  /**
   * Removes fields dynamically from the form.
   */
  @Input("removeFields")
  public set removeFields(value) {
    angularWidgetBridgeInput(value, this.removeFieldsChannel, this.unsubscribe);
  }
  /**
   * Removes all fields dynamically from the form.
   */
  @Input("removeAllFields")
  public set removeAllFields(value) {
    angularWidgetBridgeInput(
      value,
      this.removeAllFieldsChannel,
      this.unsubscribe
    );
  }
  /**
   * Triggers validation for form fields.
   */
  @Input("triggerValidation")
  public set triggerValidation(value) {
    angularWidgetBridgeInput(
      value,
      this.triggerValidationChannel,
      this.unsubscribe
    );
  }
  @ViewChild("datepicker", { static: false })
  datePicker: OwlDateTimeComponent<Date>;
  @ViewChild("datetimepicker", { static: false })
  dateTimePicker: OwlDateTimeComponent<any>;
  public lookupOptionsInputChannel = new Subject<any>();
  public resetLookupChannel = new Subject<string>();
  public reloadLookupChannel = new Subject<string>();
  public valueSetterChannel = new Subject<{ field: string; value: any }>();
  public labelSetterChannel = new Subject<{ field: string; value: any }>();
  public setDisabledChannel = new Subject<{ field: string; value: boolean }>();
  public dialogContextChannel = new Subject<{ field: string; context: any }>();
  public resetFormFieldsChannel = new Subject<any>();
  public setHiddenChannel = new Subject<{ field: string; value: boolean }>();
  public resetChannel = new Subject<any>();
  public addFieldsChannel = new Subject<DynamicFormField[]>();
  public removeFieldsChannel = new Subject<string[]>();
  public removeAllFieldsChannel = new Subject<any>();
  public triggerValidationChannel = new Subject();
  /**
   * Emits when any field value changed.
   */
  @Output("value")
  public valueOutputEmitter = new EventEmitter<any>();
  /**
   * Emits validation result when any field value changed.
   */
  @Output("valid")
  public validOutputEmitter = new EventEmitter<any>();
  /**
   * Emits when any field is clicked.
   */
  @Output("onClick")
  public clickOutputEmitter = new EventEmitter<any>();
  /**
   * Emits when enter is pressed.
   */
  @Output("onEnterPressed")
  public enterPressedOutputEmitter = new EventEmitter<any>();
  /**
   * Emits when initialization is finished for all fields and emits also before form destroy.
   */
  @Output("init")
  public initOutputEmitter = new EventEmitter<any>();
  /**
   * Emits the dropped value when using drag and drop featuer.
   */
  @Output("droppedValue")
  public droppedValueEmitter = new EventEmitter<any>();
  constructor(
    protected cdr: ChangeDetectorRef,
    protected widgetFrameService: WidgetframeService,
    protected currentLocaleService: CurrentLocaleService,
    protected dateAdapter: DateAdapter<Date>,
    protected owldateTimeAdapter: DateTimeAdapter<any>,
    protected translateService: TranslateService,
    protected owlDateTimeIntl: OwlDateTimeIntl,
    protected dialogService: DialogService,
    protected appContext: AppContext,
    protected validationService: ValidationService
  ) {}
  public ngOnInit() {
    if (!this.fields) {
      return;
    }
    this.initFields(this.fields);
    this.reloadLookupChannel
      .pipe(takeUntil(this.unsubscribe))
      .subscribe((identifier) => {
        let field = this.fields.find((entry) => entry.field === identifier);
        if (!field) {
          field = this.fields.find((entry) => entry.type === "multi-toggler");
          if (field) {
            field = field.items.find((item) => item.field === identifier);
          }
          if (!field) {
            console.error(
              `[ReloadLookup] Cant find field with identifier ${identifier}`
            );
            return;
          }
        }
        this.loadLookupData(field);
      });
    this.addFieldsChannel
      .pipe(
        withLatestFrom(this.currentLocaleService.getCurrentLocale()),
        takeUntil(this.unsubscribe)
      )
      .subscribe((data) => {
        const fields = data[0];
        const locale = data[1];
        this.fields.push(...fields);
        this.initFields(fields);
        this.updateLocale(locale, fields);
        this.cdr.markForCheck();
      });
    this.valueSetterChannel
      .pipe(takeUntil(this.unsubscribe))
      .subscribe((data) => {
        if (!this.value) {
          this.value = {};
        }
        const field = this.findField(data.field);
        if (!field) {
          return;
        }
        this.setFieldDescription(field, data.value);
        this.value[field.field] = data.value;
        this.onValueChange(field, false);
        this.cdr.markForCheck();
      });
    this.labelSetterChannel
      .pipe(takeUntil(this.unsubscribe))
      .subscribe((data) => {
        const field = this.findField(data.field);
        if (!field) {
          return;
        }
        if (field.label != null) {
          field.label = data.value;
        }
        this.cdr.markForCheck();
      });
    this.setDisabledChannel
      .pipe(takeUntil(this.unsubscribe))
      .subscribe((data) => {
        const field = this.findField(data.field);
        if (!field) {
          return;
        }
        field.disabled = data.value;
        this.cdr.markForCheck();
      });
    this.dialogContextChannel
      .pipe(takeUntil(this.unsubscribe))
      .subscribe((data) => {
        const field = this.findField(data.field);
        if (!field) {
          return;
        }
        field.dialogContext = data.context;
        this.cdr.markForCheck();
      });
    this.resetFormFieldsChannel
      .pipe(takeUntil(this.unsubscribe))
      .subscribe((data) => {
        this.fields.forEach((field) => {
          if (field.type === "category-selector") {
            field.includeSubCategories = false;
            this.cdr.markForCheck();
          }
        });
        Object.keys(this.value).forEach((field) => {
          this.value[field] = null;
          this.description[field] = null;
          this.cdr.markForCheck();
          this.resetDateValue.next(true);
        });
        this.dialogSelectedItems = {};
      });
    this.setHiddenChannel
      .pipe(takeUntil(this.unsubscribe))
      .subscribe((data) => {
        const field = this.fields.find((entry) => entry.field === data.field);
        if (!field) {
          console.error("Unable to find field " + data.field);
          return;
        }
        field.hidden = data.value;
        this.cdr.markForCheck();
      });
    this.lookupOptionsInputChannel
      .pipe(takeUntil(this.unsubscribe))
      .subscribe((data) => {
        this.setLookupOptions(data.field, data.value);
      });
    this.resetChannel.pipe(takeUntil(this.unsubscribe)).subscribe((reset) => {
      this.value = {};
      this.dialogSelectedItems = {};
      this.description = {};
      this.cdr.markForCheck();
    });
    this.resetLookupChannel
      .pipe(takeUntil(this.unsubscribe))
      .subscribe((data) => {
        this.lookupOptions[data] = [];
        this.value[data] = null;
        this.cdr.markForCheck();
      });
    this.removeFieldsChannel
      .pipe(takeUntil(this.unsubscribe))
      .subscribe((fields) => {
        this.fields = this.fields.filter((f) => fields.indexOf(f.field) === -1);
        fields.forEach((f) => delete this.value[f]);
        this.cdr.markForCheck();
      });
    this.removeAllFieldsChannel
      .pipe(takeUntil(this.unsubscribe))
      .subscribe((data) => {
        this.fields = [];
        this.value = {};
        this.description = {};
        this.cdr.markForCheck();
      });
    this.triggerValidationChannel
      .pipe(takeUntil(this.unsubscribe))
      .subscribe((data) => this.onTriggerValidation());
    this.currentLocaleService
      .getCurrentLocale()
      .pipe(takeUntil(this.unsubscribe))
      .subscribe((locale) => {
        this.updateLocale(locale, this.fields);
      });
    this.initOutputEmitter.next({ init: true });
  }
  private findField(identifier: string) {
    let field = this.fields.find((entry) => entry.field === identifier);
    if (!field) {
      field = this.fields.find((entry) => entry.type === "multi-toggler");
      if (field) {
        field = field.items.find((item) => item.field === identifier);
      }
      if (!field) {
        console.error(
          `Unable to find field with identifier ${identifier} `,
          this.fields
        );
        return;
      }
    }
    return field;
  }
  private initFields(fields: any[]) {
    this.fields.sort((a, b) => {
      if (!a.order) {
        a.order = 0;
      }
      if (!b.order) {
        b.order = 0;
      }
      return a.order - b.order;
    });
    fields.forEach((field) => {
      if (field.chipRemovable === undefined || field.chipRemovable === null) {
        field.chipRemovable = true;
      }
      if (field.clearable == null) {
        field.clearable = true;
      }
      if (field.type === "date") {
        if (!field.dateFormat) {
          field.dateFormat = "dd.mm.yyyy";
        }
      } else if (field.type === "text") {
        if (field.height) {
          //This will only work if all editors are supposed to have the same height..
          this.editorConfig = Object.assign(
            { height: field.height },
            this.editorConfig
          );
        }
      } else if (
        field.type === "lookup" ||
        field.type === "multi-lookup" ||
        field.type === "context"
      ) {
        this.loadLookupData(field);
      } else if (field.type === "category-selector") {
        if (field.publication) {
          this.value[field.field] = {
            publication: field.publication,
          };
        }
      } else if (field.type === "boolean-indeterminate") {
        this.value[field.field] = field.defaultValue;
      } else if (field.type === "password-confirmation") {
        if (field.hideConfirmationField === undefined) {
          field.hideConfirmationField = true;
        }
      } else if (field.type === "radio") {
        this.loadLookupData(field);
      } else if (field.type === "source-code-editor") {
        this.value[field.field] = field.defaultValue;
      }
      field.showHint = !field.hintOnFocus;
      this.setFieldDefaultValue(field);
      this.onValueChange(field, false);
    });
    this.currentLocaleService.getCurrentLocale().subscribe((locale) => {
      this.currentLocale = locale;
    });
  }
  private setFieldDefaultValue(field: DynamicFormField) {
    if (!field.defaultValue) {
      return;
    }
    this.value[field.field] = field.defaultValue;
  }
  private setFieldDescription(field: DynamicFormField, value: any) {
    if (!field) {
      return;
    }
    if (field.type === "user-right") {
      this.description[field.field] = value
        ? value.identifier + " (" + value.pimRef + ")"
        : null;
    } else if (field.type === "worklist") {
      this.description[field.field] = value ? value.name : null;
    } else if (field.type === "page-dialog") {
      this.description[field.field] = value
        ? value[field.dialogDescription]
        : null;
    }
  }
  private setLookupOptions(field, value) {
    this.lookupOptions[field] = value;
    this.cdr.markForCheck();
  }
  public getClass(field: DynamicFormField) {
    return String(field.field);
  }
  public getIdentifierField(field: DynamicFormField) {
    if (field.identifierField) {
      return field.identifierField;
    }
    return "identifier";
  }
  public getDescriptionField(field: DynamicFormField) {
    if (field.descriptionField) {
      return field.descriptionField;
    }
    return "description";
  }
  public loadLookupData(field: DynamicFormField) {
    if (field.lookupUrl) {
      this.widgetFrameService.getData(field.lookupUrl).subscribe((data) => {
        if (data._embedded) {
          if (data.type) {
            data = data._embedded[data.type];
          } else {
            const key = Object.keys(data._embedded)[0];
            data = data._embedded[key];
          }
        }
        if (field.lookupAllowEmpty) {
          const emptyEntry = {};
          emptyEntry[this.getIdentifierField(field)] = "";
          emptyEntry[this.getDescriptionField(field)] = "";
          data.unshift(emptyEntry);
        }
        this.setLookupOptions(field.field, data);
      });
    } else if (field.lookupOptions) {
      this.setLookupOptions(field.field, field.lookupOptions);
    }
  }
  ngOnDestroy(): void {
    this.initOutputEmitter.next({ init: false, value: this.value });
    this.unsubscribe.destroy();
  }
  onValueChange(field: DynamicFormField, isEdit: boolean = true) {
    this.onTriggerValidation();
    this.valueOutputEmitter.next({
      value: this.value,
      lastChange: field.field,
      isEdit: isEdit,
    });
  }
  onTriggerValidation() {
    const valid = this.validateFields();
    this.validOutputEmitter.next(valid);
  }
  onEditorValueChange(field: DynamicFormField, value: any) {
    this.value[field.field] = value.value;
    this.sourceCodeValidations[field.field] = value.isValid;
    this.onValueChange(field);
  }
  private validateFields(): boolean {
    this.validationService.validateDynamicFormFields(this.fields, this.value);
    let valid = true;
    for (const field of this.fields) {
      // the validation service updates the errors to field.errors
      // if field.errors is undefined or has no entries, the field is valid
      if (Array.isArray(field.errors) && field.errors.length > 0) {
        valid = false;
        break;
      }
      if (!field.required) {
        continue;
      }
      const value = this.value[field.field];
      if (
        value === undefined ||
        value === null ||
        value === "" ||
        value.length === 0
      ) {
        valid = false;
        break;
      }
      if (!this.validateLocalizedText(field)) {
        valid = false;
        break;
      }
      if (!this.validateCategorySelector(field)) {
        valid = false;
        break;
      }
      if (!this.validatePasswords(field)) {
        valid = false;
        break;
      }
      if (!this.validateSourceCodeEditor(field)) {
        valid = false;
        break;
      }
    }
    return valid;
  }
  private validateSourceCodeEditor(field) {
    if (field.type !== "source-code-editor") {
      return true;
    }
    return this.sourceCodeValidations[field.field];
  }
  private validateLocalizedText(field: DynamicFormField): boolean {
    if (!field.localizedText) {
      return true;
    }
    const value = this.value[field.field];
    let validLocalizedText = false;
    Object.keys(value).forEach((locale) => {
      if (locale && value[locale].trim()) {
        validLocalizedText = true;
      }
    });
    return validLocalizedText;
  }
  private validateCategorySelector(field: DynamicFormField): boolean {
    if (field.type !== "category-selector") {
      return true;
    }
    const value = this.value[field.field];
    if (!value.publication || !value.category || value.category.length === 0) {
      return false;
    }
    return true;
  }
  private validatePasswords(field: DynamicFormField): boolean {
    if (field.type !== "password") {
      return true;
    }
    const password = this.value[field.field];
    const confirmPassword = this.passwordConfirmation[field.field];
    if (!password || !confirmPassword) {
      return false;
    }
    return password === confirmPassword;
  }
  onClick(event, field: DynamicFormField) {
    event.stopPropagation();
    this.clickOutputEmitter.next(field);
  }
  onEnterPressed(event) {
    if (event.target) {
      // due to ngModelOptions: force blur event to trigger setting of asset-search´s formValue
      event.target.blur();
      this.enterPressedOutputEmitter.next(event.target.value);
    }
  }
  onKeyPressedDatePicker(event) {
    const key = event.key.toLowerCase();
    switch (key) {
      case "space":
      case "spacebar":
      case " ":
      case "enter":
      case "down":
      case "arrowdown":
        this.datePicker.open();
        this.onEnterPressed(event);
    }
  }
  onKeyPressedDateTimePicker(event) {
    const key = event.key.toLowerCase();
    switch (key) {
      case "space":
      case "spacebar":
      case " ":
      case "enter":
      case "down":
      case "arrowdown":
        this.dateTimePicker.open();
        this.onEnterPressed(event);
    }
  }
  public onExternalModelChange(field: DynamicFormField, value: any) {
    this.value[field.field] = value;
    this.onValueChange(field);
  }
  public openSelectDialog(field: DynamicFormField) {
    const dialogConfig = {
      minWidth: "900px",
      minHeight: "750px",
      data: {
        title: field.label,
        itemsUrl: field.lookupUrl,
        itemType: field.selectDialogItemType,
        itemTypeId: this.getIdentifierField(field),
        itemTypeDescription: this.getDescriptionField(field),
        additionalProperties: field.additionalProperties
          ? field.additionalProperties
          : [],
        preselectedItems: this.dialogSelectedItems[field.field],
        excludePreselected: false,
        allowNoSelection: true,
      },
    };
    let dialogRef = this.dialogService.open(
      SelectItemsDialogComponent,
      dialogConfig
    );
    dialogRef.afterClosed().subscribe((selectedItems) => {
      if (!selectedItems) {
        return;
      }
      if (selectedItems.length === 0) {
        this.value[field.field] = null;
        this.description[field.field] = null;
        this.dialogSelectedItems[field.field] = null;
      } else if (selectedItems.length > 0) {
        this.dialogSelectedItems[field.field] = selectedItems;
        let values = [];
        let descriptions = [];
        selectedItems.forEach((item) => {
          values.push(item[this.getIdentifierField(field)]);
          descriptions.push(item[this.getDescriptionField(field)]);
        });
        this.value[field.field] = values.toString();
        this.description[field.field] = descriptions.join(", ");
        this.onValueChange(field);
      }
      this.cdr.markForCheck();
    });
  }
  public openUserRightDialog(field: DynamicFormField) {
    const context = { userRightsUrl: field.url };
    this.pageDialog(field, "core", "select-userRight", context, "650px");
  }
  public openWorklistDialog(field: DynamicFormField) {
    this.pageDialog(field, "core", "select-worklist", null, "650px");
  }
  public openPageDialog(field: DynamicFormField) {
    this.pageDialog(
      field,
      field.dialogModule,
      field.dialogIdentifier,
      field.dialogContext,
      "900px"
    );
  }
  private pageDialog(
    field: DynamicFormField,
    module: string,
    identifier: string,
    context: any,
    minWidth: string
  ) {
    if (field.disabled) {
      return;
    }
    const dialogRef = this.dialogService.open(PageDialogComponent, {
      minWidth: minWidth,
      height: "700px",
      data: {
        title: field.label,
        acceptEnabled: true,
        customAcceptIcon: true,
        acceptIcon: "check",
        acceptText: "button.accept",
        cancelEnabled: true,
        cancelText: "button.cancel",
        module: module,
        identifier: identifier,
        context: context,
      },
    });
    dialogRef.afterClosed().subscribe((data) => {
      if (!data) {
        return;
      }
      this.setFieldDescription(field, data);
      this.value[field.field] = data;
      this.onValueChange(field);
      this.cdr.markForCheck();
    });
  }
  public clearField(field: DynamicFormField) {
    this.value[field.field] = null;
    this.description[field.field] = null;
    this.dialogSelectedItems[field.field] = null;
    this.onValueChange(field);
    this.cdr.markForCheck();
  }
  private updateLocale(locale: string, fields: DynamicFormField[]) {
    fields.forEach((field) => {
      if (field.translations) {
        const translations = field.translations;
        if (translations[locale]) {
          field.label = translations[locale];
        }
      }
      if (field.hintTranslations) {
        const translations = field.hintTranslations;
        if (translations[locale]) {
          field.hint = translations[locale];
        }
      }
    });
    this.dateAdapter.setLocale(locale);
    this.owldateTimeAdapter.setLocale(locale);
    this.owlDateTimeIntl.cancelBtnLabel =
      this.translateService.instant("button.cancel");
    this.owlDateTimeIntl.setBtnLabel =
      this.translateService.instant("button.accept");
  }
  public includeSubCategoriesValueChanged(field: DynamicFormField, value: any) {
    if (this.value[field.field]) {
      this.value[field.field].includeSubCategories = value;
    } else {
      this.value[field.field] = { includeSubCategories: value };
    }
    field.includeSubCategories = value;
  }
  public getCategoryUrl(field: DynamicFormField) {
    return getOrDefault(field.categoryUrl, "");
  }
  public booleanValueChanged(field: DynamicFormField) {
    switch (this.value[field.field]) {
      case true: {
        this.value[field.field] = false;
        break;
      }
      case false: {
        this.value[field.field] = null;
        break;
      }
      case undefined:
      case null: {
        this.value[field.field] = true;
        break;
      }
    }
    this.onValueChange(field);
  }
  addChip(event: MatChipInputEvent, field: DynamicFormField): void {
    const input = event.input;
    const value = event.value;
    if ((value || "").trim()) {
      if (this.value[field.field]) {
        this.value[field.field].push({
          identifier: value.trim(),
          description: value.trim(),
        });
      } else {
        this.value[field.field] = [
          { identifier: value.trim(), description: value.trim() },
        ];
      }
      this.onValueChange(field);
    }
    if (input) {
      input.value = "";
    }
  }
  removeChip(chip: any, field: DynamicFormField): void {
    if (!this.value[field.field]) {
      return;
    }
    const index = this.value[field.field].indexOf(chip);
    if (index >= 0) {
      this.value[field.field].splice(index, 1);
      this.onValueChange(field);
    }
  }
  public onItemDropped(ev, field) {
    const dropValueIdentifier = getOrDefault(
      field.dropValueIdentifier,
      "identifier"
    );
    const myValue = ev.dragData.source[dropValueIdentifier];
    this.droppedValueEmitter.emit(myValue);
    const cursorPosition = ev.owner.element.nativeElement.selectionStart;
    if (this.value[field.field]) {
      var firstHalf = this.value[field.field].substring(0, cursorPosition);
      if (firstHalf) {
        firstHalf += " ";
      }
      var secondHalf = this.value[field.field].substring(
        cursorPosition,
        this.value[field.field].length
      );
      if (secondHalf) {
        secondHalf = " " + secondHalf;
      }
      this.value[field.field] = firstHalf + myValue + secondHalf;
    } else {
      this.value[field.field] = myValue;
    }
    this.onValueChange(field);
  }
  public onFileUpload(field, uploadedFile: any) {
    if (uploadedFile) {
      this.value[field.field] = uploadedFile;
    } else {
      this.value[field.field] = null;
    }
    this.onValueChange(field);
  }
  public changePassword(field) {
    field.hideConfirmationField = !field.hideConfirmationField;
    field.required = !field.required;
    field.disabled = !field.disabled;
    if (field.hideConfirmationField) {
      this.value[field.field] = null;
      this.passwordConfirmation[field.field] = null;
    }
    this.onValueChange(field);
    this.clickOutputEmitter.next(field);
  }
  public getLocalizedValue(field: DynamicFormField): string {
    if (!field || this.value[field.field] == null) {
      return "";
    }
    if (field.localizedText) {
      return this.value[field.field][this.currentLocale];
    }
    return this.value[field.field];
  }
  private initializeMenuActions(field: DynamicFormField) {
    if (field.menuInteractions) {
      this.menuActions[field.field] = this.appContext.browserContext
        .subscribe(field.menuInteractions)
        .pipe(
          takeUntil(this.unsubscribe),
          filter((contents) => !!contents),
          map((contents) => {
            const interactions = contents
              .filter(
                (entry) =>
                  entry.showAsButton == null || entry.showAsButton === false
              )
              .sort((l, r) => (r.order || 0) - (l.order || 0));
            return interactions;
          })
        );
    } else {
      this.menuActions[field.field] = EMPTY;
    }
  }
  private initializeButtonActions(field: DynamicFormField) {
    if (field.menuInteractions) {
      this.buttonActions[field.field] = this.appContext.browserContext
        .subscribe(field.menuInteractions)
        .pipe(
          takeUntil(this.unsubscribe),
          filter((contents) => !!contents),
          map((contents) => {
            return contents
              .filter((entry) => entry?.showAsButton === true)
              .sort((l, r) => (r.order || 0) - (l.order || 0));
          })
        );
    } else {
      this.buttonActions[field.field] = EMPTY;
    }
  }
  public getMenuActions(field: DynamicFormField): Observable<Content[]> {
    if (this.menuActions[field.field]) {
      return this.menuActions[field.field];
    }
    if (field.menuInteractions) {
      this.initializeMenuActions(field);
    } else {
      this.menuActions[field.field] = EMPTY;
    }
    return this.menuActions[field.field];
  }
  public getButtonActions(field: DynamicFormField): Observable<Content[]> {
    if (this.buttonActions[field.field]) {
      return this.buttonActions[field.field];
    }
    if (field.menuInteractions) {
      this.initializeButtonActions(field);
    } else {
      this.buttonActions[field.field] = EMPTY;
    }
    return this.buttonActions[field.field];
  }
  public getInteractions(
    field: DynamicFormField
  ): { menu: Observable<Content[]>; buttons: Observable<Content[]> } | null {
    if (field.menuInteractions == null) {
      return null;
    }
    return {
      menu: this.getMenuActions(field),
      buttons: this.getButtonActions(field),
    };
  }
  onKeyPressed(event: KeyboardEvent, field) {
    if (field.tab && event.code !== "Tab") {
      event.preventDefault();
      return false;
    }
    return;
  }
  onFocusOut(field: DynamicFormField) {
    field.showHint = !field.hintOnFocus;
  }
  onFocusIn(field: DynamicFormField) {
    field.showHint = true;
  }
  getPreferredPosition(field: DynamicFormField) {
    return field.additionalProperties &&
      field.additionalProperties.preferredPosition
      ? field.additionalProperties.preferredPosition
      : ["right-below", "right", "bottom"];
  }
  resetDateTime(field, event) {
    event.stopPropagation();
    this.value[field.field] = null;
    this.onValueChange(field);
  }
  handlePaste(event: ClipboardEvent) {
    stripPasteContent(event);
  }
}
export interface DynamicFormConfiguration extends BaseConfiguration {
  /**
   * Array of dynamic form fields to be added in the form.
   */
  fields: DynamicFormField[];
}
export interface DynamicFormField {
  /**
   * Type of the field. supported types are: boolean, boolean-indeterminate, category-selector,
   * chip-list, date, date-time, file-upload, heading, lookup, multi-lookup,  number,
   * number-decimal, password, plain, select-dialog, text, textarea, toggler, multi-toggler, user-right, worklist, source-code-editor
   */
  type: string;
  /**
   * Unique field identifier.
   */
  field: string;
  /**
   * collection of all data for multi-components
   */
  items?: DynamicFormField[];
  /**
   * Localized key for field label
   */
  label: string;
  /**
   * URL to load lookup options from. used with these types: lookup, multi-lookup, select-dialog.
   * (See also lookupOptions)
   */
  lookupUrl?: string;
  /**
   * Array of static lookup options. used with these types: lookup, multi-lookup, select-dialog.
   * (See also lookupUrl)
   */
  lookupOptions?: any[];
  /**
   * Allows empty selection for lookup types. @default(false)
   */
  lookupAllowEmpty?: boolean;
  /**
   * Item type used for select-dialog type.
   */
  selectDialogItemType?: string;
  /**
   * Enables\Disables the field.
   */
  disabled?: boolean;
  /**
   * Makes the field required or optional.
   */
  required?: boolean;
  /**
   * Identifier field of the object. it is used with 'lookup' and 'multi-lookup' types
   * to define identifier field. @default(identifier)
   */
  identifierField?: string;
  /**
   * Description field of the object. it is used with 'lookup' and 'multi-lookup' types
   * to define description field. @default(description)
   */
  descriptionField?: string;
  /**
   * Field height. used with 'text' type.
   */
  height?: number;
  /**
   * Hides or shows the field.
   */
  hidden?: boolean;
  /**
   * Order of the field in the form. @default(0)
   */
  order?: number;
  /**
   * If not set the version select lookup will be shown,
   * otherwise the provided version will be used and the category select lookup only will be shown.
   * Used with type 'category-selector'.
   */
  publication?: string;
  /**
   * Allows multiple category selection. Used with type 'category-selector'. @default(false)
   */
  multiCategorySelect?: boolean;
  /**
   * URL used to load version from.
   * default is 'category-select-publications' for publications
   * and 'category-select-asset-trees' for asset-trees
   * Used with type 'category-selector'.
   */
  categoryUrl?: string;
  /**
   * Label translation in other locales.
   */
  translations?: any;
  /**
   * hint translation in other locales.
   */
  hintTranslations?: any;
  /**
   * Validations to be applied on field input.
   */
  validations?: DataValidation[];
  /**
   * Date format. used with 'date' type. @default(dd.mm.yyyy)
   */
  dateFormat?: string;
  /**
   * Shows\Hides include sub-categories checkbox. used with 'category-selector' type. @default(false)
   */
  showIncludeSubCategories?: boolean;
  /**
   * Includes sub-categories value. used with 'category-selector' type.
   */
  includeSubCategories?: boolean;
  /**
   * Applies channel types filter on versions. supported values are PURCHASE, SALES, ARCHIVE.
   * used with 'category-selector' type.
   */
  filterChannelTypes?: string[];
  /**
   * Array of additional properties to view in select dialog list. used with 'select-dialog' type.
   */
  additionalProperties?: any;
  /**
   * List folder type. used with 'worklist' type.
   */
  listfolderType?: string;
  /**
   * List folder data type. used with 'worklist' type.
   */
  dataType?: string;
  /**
   * Shows floating label for the field. @default(false)
   */
  floatingLabel?: boolean;
  /**
   * The target tab. used in dynamic forms with mutliple tabs.
   * supported tabs are localizedtext, listFolder, userRights
   */
  tab?: string;
  /**
   * This property shouldn't be added in configuration. It is only used internally.
   */
  value?: any;
  /**
   * Initial value of the field.
   */
  defaultValue?: any;
  /**
   * Makes the field Readonly or editable. @default(false)
   */
  readonly?: boolean;
  /**
   * Sets autosize property for type 'textarea'.
   */
  autosize?: boolean;
  /**
   * Shows clear selection button. used with 'lookup' and 'multi-lookup' types. @default(true)
   */
  clearable?: boolean;
  /**
   * Sets maximum characters length for text types.
   */
  maxLength?: number;
  /**
   * URL to load user rights. used with 'user-right' type.
   */
  url?: string;
  /**
   * Comma-separated list of supported file types. used with 'file-upload' type.
   */
  supportedFileTypes?: string;
  /**
   * File upload url. used with 'file-upload type.
   */
  fileUploadUrl?: string;
  /**
   * Sets field as localized text. @default(false)
   */
  localizedText?: boolean;
  /**
   * Identifiers of the interactions that are supposed to be loaded for the menu.
   */
  menuInteractions?: Selectors;
  /**
   * List of field validation errors.
   */
  errors?: ValidationError[];
  /**
   * Array of supported MIME types. used with 'file-upload' type.
   */
  supportedMimeTypes?: string[];
  /**
   * Makes chips removable in 'chip-list' type. @default(true)
   */
  chipRemovable?: boolean;
  /**
   * Shows\Hides edit password button. used with 'password' type. @default(false)
   */
  showEditpasswordButton?: boolean;
  /**
   * Hides\Shows second password confirmation field. used with 'password' type. @default(true)
   */
  hideConfirmationField?: boolean;
  /**
   * Localized key for password confirmation lable. used with 'password' type.
   */
  confirmationLabel?: string;
  /**
   * Auto focus field.
   */
  autofocus?: boolean;
  /**
   * Localized key for field hint.
   */
  hint?: string;
  /**
   * Shows hint on focus.
   */
  hintOnFocus?: boolean;
  /**
   * This property shouldn't be added in configuration. It is only used internally.
   */
  showHint?: boolean;
  /**
   * Identifier field that will be used to get the value while dropping it in the textarea field.
   */
  dropValueIdentifier?: string;
  /**
   * min rows for textarea field
   */
  autosizeMinRows?: string;
  /**
   * Source code programming language. It is used with type = 'source-code-editor'.
   */
  sourceCodeLanguage?: string;
  /**
   * Dialog module for page-dialog type.
   */
  dialogModule?: string;
  /**
   * Dialog identifier for page-dialog type.
   */
  dialogIdentifier?: string;
  /**
   * Dialog data for page-dialog type.
   */
  dialogContext?: any;
  /**
   * Dialog data property used to set the dialog description when closed for page-dialog type.
   */
  dialogDescription?: any;
  /**
   * Enable/Disable editor minimap for source-code-editor type.
   */
  enableMinimap?: boolean;
  /**
   * Triggers the display of Tiny editor implmentation in its onInit callback
   */
  showEditorOnInit?: boolean;
}
<form>
  <div
    *ngFor="let field of fields"
    class="nm-dynamicFormFields nm-dynamicFormFields__formElement form-element {{
      field.required ? 'required' : 'optional'
    }} {{ field.hint && field.showHint ? 'hint' : '' }}"
    (click)="onClick($event, field)"
  >
    <ng-container *ngIf="!field.hidden" [ngSwitch]="field.type">
      <ng-container *ngSwitchCase="'context'">
        <nm-context-selector
          [contexts]="lookupOptions[field.field]"
          [placeholder]="field.label | translate"
          [field]="field.identifierField"
          [(ngModel)]="value[field.field]"
          [name]="field.field"
          [disabled]="field.disabled"
          [clearable]="field.clearable"
          (ngModelChange)="onValueChange(field)"
          [nmAutofocus]="field.autofocus"
          tabIndex="0"
        >
        </nm-context-selector>
        <div class="non-mat-form-field-hint" *ngIf="field.hint">
          {{ field.hint | translate }}
        </div>
        <ng-container
          *ngTemplateOutlet="interactionOutlet; context: { field: field }"
        ></ng-container>
      </ng-container>
      <!-- Lookup -->
      <ng-container *ngSwitchCase="'lookup'">
        <mat-form-field
          class="nm-lookup-field"
          [ngClass]="getClass(field)"
          (focusin)="onFocusIn(field)"
          (focusout)="onFocusOut(field)"
        >
          <mat-label *ngIf="field.floatingLabel">
            {{ field.label | translate }}
          </mat-label>
          <nm-combo
            class="combo"
            [options]="lookupOptions[field.field]"
            [valueKey]="getIdentifierField(field)"
            [displayKey]="getDescriptionField(field)"
            [filterPlaceholder]="'placeholder.search' | translate"
            [placeholder]="field.label | translate"
            [disabled]="field.disabled"
            [clearable]="field.clearable"
            [name]="field.field"
            [nmAutofocus]="field.autofocus"
            [(ngModel)]="value[field.field]"
            (ngModelChange)="onValueChange(field)"
            tabIndex="0"
          >
          </nm-combo>
          <mat-hint *ngIf="field.hint && field.showHint">{{
            field.hint | translate
          }}</mat-hint>
        </mat-form-field>
        <ng-container
          *ngTemplateOutlet="interactionOutlet; context: { field: field }"
        ></ng-container>
      </ng-container>
      <!-- Multi-Lookup-->
      <ng-container *ngSwitchCase="'multi-lookup'">
        <mat-form-field
          class="nm-multi-lookup-field"
          [ngClass]="getClass(field)"
          (focusin)="onFocusIn(field)"
          (focusout)="onFocusOut(field)"
        >
          <mat-label *ngIf="field.floatingLabel">
            {{ field.label | translate }}
          </mat-label>
          <nm-combo
            class="combo"
            [options]="lookupOptions[field.field]"
            [valueKey]="getIdentifierField(field)"
            [displayKey]="getDescriptionField(field)"
            [filterPlaceholder]="'placeholder.search' | translate"
            [placeholder]="field.label | translate"
            [disabled]="field.disabled"
            [clearable]="field.clearable"
            [multiple]="true"
            [name]="field.field"
            [nmAutofocus]="field.autofocus"
            [(ngModel)]="value[field.field]"
            (ngModelChange)="onValueChange(field)"
            tabIndex="0"
          >
          </nm-combo>
          <mat-hint *ngIf="field.hint && field.showHint">{{
            field.hint | translate
          }}</mat-hint>
        </mat-form-field>
        <ng-container
          *ngTemplateOutlet="interactionOutlet; context: { field: field }"
        ></ng-container>
      </ng-container>
      <!-- Date -->
      <ng-container *ngSwitchCase="'date'">
        <mat-form-field
          class="full-width nm-dynamicFormFields__date nm-date-time-input"
          [ngClass]="getClass(field)"
          (click)="datepicker.open()"
          (focusin)="onFocusIn(field)"
          (focusout)="onFocusOut(field)"
        >
          <mat-label *ngIf="field.floatingLabel"
            >{{ field.label | translate }}
          </mat-label>
          <input
            autocomplete="off"
            matInput
            [owlDateTime]="datepicker"
            [(ngModel)]="value[field.field]"
            [ngModelOptions]="{ updateOn: 'blur' }"
            (ngModelChange)="onValueChange(field)"
            placeholder="{{ field.label | translate }}"
            [disabled]="field.disabled"
            [name]="field.field"
            (keydown)="onKeyPressedDatePicker($event)"
          />
          <button
            mat-icon-button
            color="primary"
            matSuffix
            class="remove-action"
            *ngIf="value[field.field] && !field.readonly && !field.disabled"
            (click)="resetDateTime(field, $event)"
            tabIndex="-1"
          >
            <mat-icon color="primary" class="fade-in">close</mat-icon>
          </button>
          <nm-dynamic-form-errors
            *ngIf="field.errors"
            [errors]="field.errors"
            [field]="field"
          ></nm-dynamic-form-errors>
          <mat-hint *ngIf="field.hint && field.showHint">{{
            field.hint | translate
          }}</mat-hint>
          <ng-container
            *ngTemplateOutlet="interactionOutlet; context: { field: field }"
          ></ng-container>
        </mat-form-field>
        <owl-date-time #datepicker [pickerType]="'calendar'"></owl-date-time>
      </ng-container>
      <!-- plain -->
      <mat-form-field
        *ngSwitchCase="'plain'"
        [ngClass]="getClass(field)"
        (focusin)="onFocusIn(field)"
        (focusout)="onFocusOut(field)"
      >
        <mat-label *ngIf="field.floatingLabel"
          >{{ field.label | translate }}
        </mat-label>
        <input
          matInput
          type="text"
          [placeholder]="field.label | translate"
          [ngModel]="getLocalizedValue(field)"
          (ngModelChange)="onExternalModelChange(field, $event)"
          [ngModelOptions]="{ updateOn: 'blur' }"
          [disabled]="field.disabled"
          [readonly]="field.readonly"
          [maxlength]="field.maxLength"
          [name]="field.field"
          (keydown.enter)="onEnterPressed($event)"
          (keydown)="onKeyPressed($event, field)"
          [nmAutofocus]="field.autofocus"
          [matTooltip]="getLocalizedValue(field)"
          [matTooltipDisabled]="!field.disabled || !field.readonly"
        />
        <nm-dynamic-form-errors
          *ngIf="field.errors"
          [errors]="field.errors"
          [field]="field"
        ></nm-dynamic-form-errors>
        <button
          color="primary"
          mat-icon-button
          *ngIf="field.tab"
          matSuffix
          class="fade-in nm-dynamicFormFields__moreIcon"
        >
          <mat-icon>more_horiz</mat-icon>
        </button>
        <button
          *ngIf="field.clearable && !field.disabled && value[field.field]"
          mat-button
          matSuffix
          mat-icon-button
          aria-label="Clear"
          color="primary"
          class="
            mat-body mat-form-field-suffix
            nm-dynamicFormFields__clearButton
          "
          (click)="clearField(field); $event.stopPropagation()"
          tabIndex="-1"
        >
          <mat-icon>close</mat-icon>
        </button>
        <mat-hint *ngIf="field.hint && field.showHint">{{
          field.hint | translate
        }}</mat-hint>
        <ng-container
          *ngTemplateOutlet="interactionOutlet; context: { field: field }"
        ></ng-container>
      </mat-form-field>
      <!-- heading -->
      <div
        *ngSwitchCase="'heading'"
        [ngClass]="[getClass(field), 'nm-heading']"
      >
        {{ field.label | translate }}{{ value[field.field] }}
      </div>
      <!-- number -->
      <mat-form-field
        *ngSwitchCase="'number'"
        [ngClass]="getClass(field)"
        (focusin)="onFocusIn(field)"
        (focusout)="onFocusOut(field)"
      >
        <mat-label *ngIf="field.floatingLabel"
          >{{ field.label | translate }}
        </mat-label>
        <input
          matInput
          type="number"
          [placeholder]="field.label | translate"
          [(ngModel)]="value[field.field]"
          (ngModelChange)="onValueChange(field)"
          [ngModelOptions]="{ updateOn: 'blur' }"
          [disabled]="field.disabled"
          [name]="field.field"
          (keydown.enter)="onEnterPressed($event)"
          [nmAutofocus]="field.autofocus"
          [matTooltip]="value[field.field]"
          [matTooltipDisabled]="!field.disabled || !field.readonly"
        />
        <nm-dynamic-form-errors
          *ngIf="field.errors"
          [errors]="field.errors"
          [field]="field"
        ></nm-dynamic-form-errors>
        <button
          color="primary"
          mat-icon-button
          *ngIf="field.tab"
          matSuffix
          class="fade-in nm-dynamicFormFields__moreIcon"
        >
          <mat-icon>more_horiz</mat-icon>
        </button>
        <button
          *ngIf="field.clearable && !field.disabled && value[field.field]"
          mat-button
          matSuffix
          mat-icon-button
          aria-label="Clear"
          color="primary"
          class="
            mat-body mat-form-field-suffix
            nm-dynamicFormFields__clearButton
          "
          (click)="clearField(field); $event.stopPropagation()"
          tabIndex="-1"
        >
          <mat-icon>close</mat-icon>
        </button>
        <mat-hint *ngIf="field.hint && field.showHint">{{
          field.hint | translate
        }}</mat-hint>
        <ng-container
          *ngTemplateOutlet="interactionOutlet; context: { field: field }"
        ></ng-container>
      </mat-form-field>
      <!-- boolean -->
      <mat-checkbox
        color="primary"
        *ngSwitchCase="'boolean'"
        class="nm-dynamicFormFields__checkbox"
        [ngClass]="getClass(field)"
        [(ngModel)]="value[field.field]"
        name="default"
        (ngModelChange)="onValueChange(field)"
        [disabled]="field.disabled"
        [name]="field.field"
      >
        <span> {{ field.label | translate }} </span>
        <ng-container
          *ngTemplateOutlet="interactionOutlet; context: { field: field }"
        ></ng-container>
      </mat-checkbox>
      <!-- textarea -->
      <mat-form-field
        *ngSwitchCase="'textarea'"
        [ngClass]="getClass(field)"
        (focusin)="onFocusIn(field)"
        (focusout)="onFocusOut(field)"
      >
        <mat-label *ngIf="field.floatingLabel"
          >{{ field.label | translate }}
        </mat-label>
        <textarea
          (dropped)="onItemDropped($event, field)"
          (ngModelChange)="onExternalModelChange(field, $event)"
          [(ngModel)]="value[field.field]"
          [disabled]="field.disabled"
          [mat-autosize]="field.autosize"
          [maxlength]="field.maxLength"
          [name]="field.field"
          [nmAutofocus]="field.autofocus"
          [ngModelOptions]="{ updateOn: 'blur' }"
          [placeholder]="field.label | translate"
          [readonly]="field.readonly"
          cdkTextareaAutosize
          igxDrop
          matAutosizeMaxRows="20"
          [matAutosizeMinRows]="
            field.autosizeMinRows ? field.autosizeMinRows : 2
          "
          matInput
          spellcheck="true"
          [matTooltip]="value[field.field]"
          [matTooltipDisabled]="!field.disabled || !field.readonly"
          (paste)="handlePaste($event)"
        >
        {{ value[field.field] }}
      </textarea
        >
        <nm-dynamic-form-errors
          *ngIf="field.errors"
          [errors]="field.errors"
          [field]="field"
        ></nm-dynamic-form-errors>
        <button
          *ngIf="field.clearable && !field.disabled && value[field.field]"
          mat-button
          matSuffix
          mat-icon-button
          aria-label="Clear"
          color="primary"
          class="
            mat-body mat-form-field-suffix
            nm-dynamicFormFields__clearButton
          "
          (click)="clearField(field); $event.stopPropagation()"
          tabIndex="-1"
        >
          <mat-icon>close</mat-icon>
        </button>
        <mat-hint *ngIf="field.hint && field.showHint">{{
          field.hint | translate
        }}</mat-hint>
        <ng-container
          *ngTemplateOutlet="interactionOutlet; context: { field: field }"
        ></ng-container>
      </mat-form-field>
      <!--text -->
      <ng-container *ngSwitchCase="'text'">
        <nm-tiny-text-editor
          [ngClass]="getClass(field)"
          [disabled]="field.disabled"
          (valueChanged)="onExternalModelChange(field, $event)"
          [value]="value[field.field]"
          [showEditorOnInit]="field.showEditorOnInit"
        >
        </nm-tiny-text-editor>
        <ng-container
          *ngTemplateOutlet="interactionOutlet; context: { field: field }"
        ></ng-container>
      </ng-container>
      <!-- Source code editor -->
      <ng-container *ngSwitchCase="'source-code-editor'">
        <div class="nm-dynamicFormFields__sourceCodeEditor">
          <nm-source-code-editor
            [ngClass]="getClass(field)"
            [disabled]="field.disabled"
            [enableMinimap]="field.enableMinimap"
            [code]="value[field.field]"
            [sourceCodeLanguage]="field.sourceCodeLanguage"
            (valueChanged)="onEditorValueChange(field, $event)"
          >
          </nm-source-code-editor>
          <ng-container
            *ngTemplateOutlet="interactionOutlet; context: { field: field }"
          ></ng-container>
        </div>
      </ng-container>
      <!-- select-dialog -->
      <mat-form-field
        *ngSwitchCase="'select-dialog'"
        [ngClass]="getClass(field)"
        (focusin)="onFocusIn(field)"
        (focusout)="onFocusOut(field)"
      >
        <mat-label *ngIf="field.floatingLabel"
          >{{ field.label | translate }}
        </mat-label>
        <input
          matInput
          type="text"
          [placeholder]="field.label | translate"
          [ngModel]="description[field.field]"
          (ngModelChange)="onValueChange(field)"
          readonly
          [name]="field.field"
        />
        <button
          mat-icon-button
          color="primary"
          matSuffix
          (click)="openSelectDialog(field)"
          [disabled]="field.disabled"
        >
          <mat-icon
            class="
              fade-in
              nm-dynamicFormFields__moreIcon nm-dynamicFormFields__moreIcon
            "
          >
            more_horiz
          </mat-icon>
        </button>
        <button
          *ngIf="field.clearable && !field.disabled && value[field.field]"
          mat-button
          matSuffix
          mat-icon-button
          aria-label="Clear"
          color="primary"
          class="
            mat-body mat-form-field-suffix
            nm-dynamicFormFields__clearButton
          "
          (click)="clearField(field); $event.stopPropagation()"
          tabIndex="-1"
        >
          <mat-icon>close</mat-icon>
        </button>
        <mat-hint *ngIf="field.hint && field.showHint">{{
          field.hint | translate
        }}</mat-hint>
        <ng-container
          *ngTemplateOutlet="interactionOutlet; context: { field: field }"
        ></ng-container>
      </mat-form-field>
      <!-- Category-selector -->
      <ng-container *ngSwitchCase="'category-selector'">
        <mat-label
          class="nm-dynamicFormFields__categorySelectorLabel"
          *ngIf="!field.floatingLabel"
        >
          {{ field.label | translate }}
        </mat-label>
        <nm-category-select-control
          [placeholder]="field.placeholder | translate"
          [categoryPlaceholder]="field.categoryPlaceholder | translate"
          [value]="value[field.field]"
          (change)="onExternalModelChange(field, $event)"
          [localStorageRecovery]="false"
          [showPublicationDropdown]="!field.publication"
          [multi]="field.multiCategorySelect"
          [showIncludeSubCategories]="field.showIncludeSubCategories"
          [includeSubCategories]="field.includeSubCategories"
          [categoryUrl]="field.categoryUrl"
          (includeSubCategoriesChange)="
            includeSubCategoriesValueChanged(field, $event)
          "
          [filterChannelTypes]="field.filterChannelTypes"
          [preferredPosition]="getPreferredPosition(field)"
          [autofocus]="field.autofocus"
        >
        </nm-category-select-control>
        <ng-container
          *ngTemplateOutlet="interactionOutlet; context: { field: field }"
        ></ng-container>
      </ng-container>
      <!-- toggle / toggler -->
      <ng-container *ngSwitchCase="'toggler'">
        <mat-slide-toggle
          [ngClass]="getClass(field)"
          [(ngModel)]="value[field.field]"
          (ngModelChange)="onValueChange(field)"
          [disabled]="field.disabled"
          [name]="field.field"
          >{{ field.label | translate }}
        </mat-slide-toggle>
        <ng-container
          *ngTemplateOutlet="interactionOutlet; context: { field: field }"
        ></ng-container>
      </ng-container>
      <!-- multi-toggle / multiToggler -->
      <ng-container *ngSwitchCase="'multi-toggler'">
        <ng-container *ngFor="let item of field.items">
          <mat-slide-toggle
            class="nm-dynamicFormFields__multi-toggler"
            [ngClass]="getClass(field)"
            [(ngModel)]="value[item.field]"
            (ngModelChange)="onValueChange(item)"
            [disabled]="item.disabled"
            [name]="item.field"
            >{{ item.label | translate }}
          </mat-slide-toggle>
        </ng-container>
      </ng-container>
      <!-- user-right -->
      <mat-form-field
        *ngSwitchCase="'user-right'"
        [ngClass]="getClass(field)"
        (focusin)="onFocusIn(field)"
        (focusout)="onFocusOut(field)"
      >
        <mat-label *ngIf="field.floatingLabel"
          >{{ field.label | translate }}
        </mat-label>
        <button
          mat-icon-button
          color="primary"
          matSuffix
          class="remove-action"
          *ngIf="description[field.field]"
          (click)="clearField(field)"
          [disabled]="field.disabled"
          tabIndex="-1"
        >
          <mat-icon class="fade-in">close</mat-icon>
        </button>
        <input
          matInput
          type="text"
          [placeholder]="field.label | translate"
          [ngModel]="description[field.field]"
          (ngModelChange)="onValueChange(field)"
          (click)="openUserRightDialog(field)"
          [name]="field.field"
          readonly
        />
        <button
          mat-icon-button
          color="primary"
          matSuffix
          (click)="openUserRightDialog(field)"
          [disabled]="field.disabled"
        >
          <mat-icon class="fade-in nm-dynamicFormFields__moreIcon"
            >more_horiz</mat-icon
          >
        </button>
        <mat-hint *ngIf="field.hint && field.showHint">{{
          field.hint | translate
        }}</mat-hint>
        <ng-container
          *ngTemplateOutlet="interactionOutlet; context: { field: field }"
        ></ng-container>
      </mat-form-field>
      <!-- worklist -->
      <mat-form-field
        *ngSwitchCase="'worklist'"
        [ngClass]="getClass(field)"
        (focusin)="onFocusIn(field)"
        (focusout)="onFocusOut(field)"
      >
        <mat-label *ngIf="field.floatingLabel"
          >{{ field.label | translate }}
        </mat-label>
        <button
          mat-icon-button
          color="primary"
          matSuffix
          class="remove-action"
          *ngIf="description[field.field]"
          (click)="clearField(field)"
          [disabled]="field.disabled"
        >
          <mat-icon class="fade-in">close</mat-icon>
        </button>
        <input
          matInput
          type="text"
          [placeholder]="field.label | translate"
          [ngModel]="description[field.field]"
          (ngModelChange)="onValueChange(field)"
          (click)="openWorklistDialog(field)"
          [name]="field.field"
          readonly
          [disabled]="field.disabled"
        />
        <button
          mat-icon-button
          color="primary"
          matSuffix
          (click)="openWorklistDialog(field)"
          [disabled]="field.disabled"
        >
          <mat-icon class="fade-in nm-dynamicFormFields__moreIcon"
            >more_horiz</mat-icon
          >
        </button>
        <mat-hint *ngIf="field.hint && field.showHint">{{
          field.hint | translate
        }}</mat-hint>
        <ng-container
          *ngTemplateOutlet="interactionOutlet; context: { field: field }"
        ></ng-container>
      </mat-form-field>
      <!-- number-decimal -->
      <mat-form-field
        *ngSwitchCase="'number-decimal'"
        [ngClass]="getClass(field)"
        (focusin)="onFocusIn(field)"
        (focusout)="onFocusOut(field)"
      >
        <mat-label *ngIf="field.floatingLabel"
          >{{ field.label | translate }}
        </mat-label>
        <input
          matInput
          type="number"
          [placeholder]="field.label | translate"
          [(ngModel)]="value[field.field]"
          (ngModelChange)="onValueChange(field)"
          [ngModelOptions]="{ updateOn: 'blur' }"
          [disabled]="field.disabled"
          [name]="field.field"
          step="0.01"
          (keydown.enter)="onEnterPressed($event)"
        />
        <nm-dynamic-form-errors
          *ngIf="field.errors"
          [errors]="field.errors"
          [field]="field"
        ></nm-dynamic-form-errors>
        <button
          *ngIf="field.clearable && !field.disabled && value[field.field]"
          mat-button
          matSuffix
          mat-icon-button
          aria-label="Clear"
          color="primary"
          class="
            mat-body mat-form-field-suffix
            nm-dynamicFormFields__clearButton
          "
          (click)="clearField(field); $event.stopPropagation()"
          tabIndex="-1"
        >
          <mat-icon>close</mat-icon>
        </button>
        <mat-hint *ngIf="field.hint && field.showHint">{{
          field.hint | translate
        }}</mat-hint>
        <ng-container
          *ngTemplateOutlet="interactionOutlet; context: { field: field }"
        ></ng-container>
      </mat-form-field>
      <!-- Date-time -->
      <ng-container *ngSwitchCase="'date-time'">
        <mat-form-field
          class="full-width nm-date-time-input"
          [ngClass]="getClass(field)"
          (click)="datetimepicker.open()"
          (focusin)="onFocusIn(field)"
          (focusout)="onFocusOut(field)"
        >
          <mat-label *ngIf="field.floatingLabel"
            >{{ field.label | translate }}
          </mat-label>
          <input
            autocomplete="off"
            matInput
            [owlDateTime]="datetimepicker"
            [(ngModel)]="value[field.field]"
            [ngModelOptions]="{ updateOn: 'blur' }"
            (ngModelChange)="onValueChange(field)"
            placeholder="{{ field.label | translate }}"
            [disabled]="field.disabled"
            [name]="field.field"
            (keydown)="onKeyPressedDateTimePicker($event)"
          />
          <button
            mat-icon-button
            color="primary"
            matSuffix
            class="remove-action"
            *ngIf="value[field.field] && !field.readonly && !field.disabled"
            (click)="resetDateTime(field, $event)"
            tabIndex="-1"
          >
            <mat-icon color="primary" class="fade-in">close</mat-icon>
          </button>
          <nm-dynamic-form-errors
            *ngIf="field.errors"
            [errors]="field.errors"
            [field]="field"
          ></nm-dynamic-form-errors>
          <mat-hint *ngIf="field.hint && field.showHint">{{
            field.hint | translate
          }}</mat-hint>
          <ng-container
            *ngTemplateOutlet="interactionOutlet; context: { field: field }"
          ></ng-container>
        </mat-form-field>
        <owl-date-time #datetimepicker></owl-date-time>
      </ng-container>
      <!-- boolean-indeterminate -->
      <mat-checkbox
        *ngSwitchCase="'boolean-indeterminate'"
        name="default"
        color="primary"
        class="align-checkbox nm-boolean-indeterminate-field"
        [ngClass]="getClass(field)"
        [ngModel]="value[field.field]"
        [indeterminate]="value[field.field] === null"
        (ngModelChange)="booleanValueChanged(field)"
        [disabled]="field.disabled"
        [name]="field.field"
      >
        <span> {{ field.label | translate }} </span>
        <ng-container
          *ngTemplateOutlet="interactionOutlet; context: { field: field }"
        ></ng-container>
      </mat-checkbox>
      <!-- radio -->
      <mat-radio-group
        *ngSwitchCase="'radio'"
        color="primary"
        class="nm-dynamicFormFields__radioButtonGroup"
        [ngClass]="getClass(field)"
        [(ngModel)]="value[field.field]"
        (change)="onValueChange(field)"
        [disabled]="field.disabled"
        [name]="field.field"
      >
        <div class="nm-dynamicFormFields__radioButtonClear">
          <mat-label class="mat-label" *ngIf="field.floatingLabel">
            {{ field.label | translate }}
          </mat-label>
          <button
            *ngIf="field.clearable && !field.disabled && value[field.field]"
            mat-button
            matSuffix
            mat-icon-button
            aria-label="Clear"
            color="primary"
            class="
              mat-body mat-form-field-suffix
              nm-dynamicFormFields__clearButton
            "
            (click)="clearField(field); $event.stopPropagation()"
            tabIndex="-1"
          >
            <mat-icon>close</mat-icon>
          </button>
        </div>
        <mat-radio-button
          class="nm-dynamicFormFields__radioButton"
          *ngFor="let option of lookupOptions[field.field]"
          [disabled]="!option.enabled"
          [value]="option.value"
        >
          {{ option.description | translate }}
        </mat-radio-button>
        <nm-dynamic-form-errors
          *ngIf="field.errors"
          [errors]="field.errors"
          [field]="field"
        >
        </nm-dynamic-form-errors>
        <mat-hint *ngIf="field.hint && field.showHint">{{
          field.hint | translate
        }}</mat-hint>
      </mat-radio-group>
      <!-- chip-list -->
      <mat-form-field
        *ngSwitchCase="'chip-list'"
        class="nm-chip-list-field"
        [ngClass]="getClass(field)"
        (focusin)="onFocusIn(field)"
        (focusout)="onFocusOut(field)"
      >
        <mat-label *ngIf="field.floatingLabel">{{
          field.label | translate
        }}</mat-label>
        <mat-chip-list #chipList>
          <mat-chip
            *ngFor="let chip of value[field.field]"
            [id]="chip[getIdentifierField(field)]"
            [removable]="true"
            (removed)="removeChip(chip, field)"
          >
            <span>{{ chip[getDescriptionField(field)] }}</span>
            <mat-icon *ngIf="field.chipRemovable" matChipRemove
              >cancel
            </mat-icon>
          </mat-chip>
          <input
            [matChipInputFor]="chipList"
            [matChipInputSeparatorKeyCodes]="separatorKeysCodes"
            [matChipInputAddOnBlur]="true"
            (matChipInputTokenEnd)="addChip($event, field)"
            [disabled]="field.disabled"
            [readonly]="field.readonly"
            [name]="field.field"
            [nmAutofocus]="field.autofocus"
            autocomplete="off"
          />
        </mat-chip-list>
        <button
          color="primary"
          mat-icon-button
          *ngIf="field.tab"
          matSuffix
          class="fade-in nm-dynamicFormFields__moreIcon"
        >
          <mat-icon>more_horiz</mat-icon>
        </button>
        <button
          *ngIf="field.clearable && !field.disabled && value[field.field]"
          mat-button
          matSuffix
          mat-icon-button
          aria-label="Clear"
          color="primary"
          class="
            mat-body mat-form-field-suffix
            nm-dynamicFormFields__clearButton
          "
          (click)="clearField(field); $event.stopPropagation()"
          tabIndex="-1"
        >
          <mat-icon>close</mat-icon>
        </button>
        <mat-hint *ngIf="field.hint && field.showHint">{{
          field.hint | translate
        }}</mat-hint>
        <ng-container
          *ngTemplateOutlet="interactionOutlet; context: { field: field }"
        ></ng-container>
      </mat-form-field>
      <!-- password -->
      <ng-container
        *ngSwitchCase="'password'"
        [ngClass]="getClass(field)"
        (focusin)="onFocusIn(field)"
        (focusout)="onFocusOut(field)"
      >
        <mat-form-field>
          <mat-label *ngIf="field.floatingLabel"
            >{{ field.label | translate }}
          </mat-label>
          <input
            autocomplete="off"
            matInput
            [type]="showPassword ? 'text' : 'password'"
            [placeholder]="field.label | translate"
            [(ngModel)]="value[field.field]"
            (ngModelChange)="onValueChange(field)"
            [ngModelOptions]="{ updateOn: 'blur' }"
            [disabled]="field.disabled"
            [readonly]="field.readonly"
            [name]="field.field"
            (keydown.enter)="onEnterPressed($event)"
          />
          <button
            mat-icon-button
            color="primary"
            matSuffix
            (click)="showPassword = !showPassword"
          >
            <mat-icon
              >{{ showPassword ? "visibility" : "visibility_off" }}
            </mat-icon>
          </button>
          <button
            mat-icon-button
            color="primary"
            matSuffix
            *ngIf="field.showEditpasswordButton"
            (click)="changePassword(field)"
          >
            <mat-icon class="fade-in">edit</mat-icon>
          </button>
          <nm-dynamic-form-errors
            *ngIf="field.errors"
            [errors]="field.errors"
            [field]="field"
          ></nm-dynamic-form-errors>
          <mat-hint *ngIf="field.hint && field.showHint">{{
            field.hint | translate
          }}</mat-hint>
          <ng-container
            *ngTemplateOutlet="interactionOutlet; context: { field: field }"
          ></ng-container>
        </mat-form-field>
        <div [hidden]="field.hideConfirmationField">
          <mat-form-field>
            <mat-label *ngIf="field.floatingLabel"
              >{{ field.confirmationLabel | translate }}
            </mat-label>
            <input
              autocomplete="off"
              matInput
              type="password"
              [(ngModel)]="passwordConfirmation[field.field]"
              (ngModelChange)="onValueChange(field)"
              [disabled]="field.disabled"
              [readonly]="field.readonly"
              [name]="field.field"
              (keydown.enter)="onEnterPressed($event)"
            />
          </mat-form-field>
        </div>
      </ng-container>
      <!-- file-upload -->
      <ng-container *ngSwitchCase="'file-upload'">
        <nm-upload-file
          [ngClass]="getClass(field)"
          [supportedFileTypes]="field.supportedFileTypes"
          [supportedMimeTypes]="field.supportedMimeTypes"
          (fileContent)="onFileUpload(field, $event)"
          [disabled]="field.disabled"
          [uploadUrl]="field.fileUploadUrl"
          [value]="value[field.field]"
        >
        </nm-upload-file>
        <ng-container
          *ngTemplateOutlet="interactionOutlet; context: { field: field }"
        ></ng-container>
        <button
          *ngIf="field.clearable && !field.disabled && value[field.field]"
          mat-button
          matSuffix
          mat-icon-button
          aria-label="Clear"
          color="primary"
          class="
            mat-body mat-form-field-suffix
            nm-dynamicFormFields__clearButton
          "
          (click)="clearField(field); $event.stopPropagation()"
        >
          <mat-icon>close</mat-icon>
        </button>
      </ng-container>
      <!-- page-dialog -->
      <mat-form-field
        *ngSwitchCase="'page-dialog'"
        [ngClass]="getClass(field)"
        (focusin)="onFocusIn(field)"
        (focusout)="onFocusOut(field)"
      >
        <mat-label *ngIf="field.floatingLabel"
          >{{ field.label | translate }}
        </mat-label>
        <input
          matInput
          type="text"
          [placeholder]="field.label | translate"
          [ngModel]="description[field.field]"
          (ngModelChange)="onValueChange(field)"
          readonly
          [name]="field.field"
        />
        <button
          mat-icon-button
          color="primary"
          matSuffix
          (click)="openPageDialog(field)"
          [disabled]="field.disabled"
        >
          <mat-icon
            class="
              fade-in
              nm-dynamicFormFields__moreIcon nm-dynamicFormFields__moreIcon
            "
          >
            more_horiz
          </mat-icon>
        </button>
        <button
          *ngIf="field.clearable && !field.disabled && value[field.field]"
          mat-button
          matSuffix
          mat-icon-button
          aria-label="Clear"
          color="primary"
          class="
            mat-body mat-form-field-suffix
            nm-dynamicFormFields__clearButton
          "
          (click)="clearField(field); $event.stopPropagation()"
          tabIndex="-1"
        >
          <mat-icon>close</mat-icon>
        </button>
        <mat-hint *ngIf="field.hint && field.showHint">{{
          field.hint | translate
        }}</mat-hint>
        <ng-container
          *ngTemplateOutlet="interactionOutlet; context: { field: field }"
        ></ng-container>
      </mat-form-field>
      <div *ngSwitchDefault>Not configured: {{ field.type }}</div>
    </ng-container>
  </div>
</form>
<ng-template #interactionOutlet let-field="field">
  <nm-interaction-menu-bar
    *ngIf="field.menuInteractions"
    [menu]="getMenuActions(field) | async"
    [buttons]="getButtonActions(field) | async"
    [buttonActionTemplate]="buttonAction"
    [menuConfigurationTemplate]="field.customMenuTemplate ? menuTemplate : ''"
    [param]="{ value: value, field: field }"
  >
  </nm-interaction-menu-bar>
</ng-template>
<ng-template
  #buttonAction
  let-onClick="onClick"
  let-configuration="configuration"
  let-param="param"
  let-disabled="disabled"
>
  <nm-button
    [disabled]="disabled | async"
    (fireEvent)="onClick($event)"
    [buttonType]="
      configuration.buttonType ? configuration.buttonType : 'mat-button'
    "
    [icon]="configuration.icon"
    [buttonColor]="configuration.buttonColor ? configuration.buttonColor : ''"
    [placeholder]="configuration.description | translate"
  >
  </nm-button>
</ng-template>
<ng-template
  #menuTemplate
  let-onClick="onClick"
  let-configuration="configuration"
  let-components="param.field.templateComponents"
>
  <nm-container
    *ngFor="let component of components"
    [configuration]="component | widgetFor: component.configuration"
    [id]="component.id"
  >
  </nm-container>
</ng-template>