File

libs/core/dynamic-form/date/src/date.component.ts

Extends

DfBaseComponent

Implements

OnInit AfterViewInit

Metadata

Index

Properties
Methods

Constructor

constructor(parseFormatToken: string, formfieldDefaultOptions: FormfieldDefaultOptions, el: ElementRef)
Parameters :
Name Type Optional
parseFormatToken string No
formfieldDefaultOptions FormfieldDefaultOptions No
el ElementRef No

Methods

addPanelClass
addPanelClass(configValue: DfDateConfig, panelClass: string)
Parameters :
Name Type Optional
configValue DfDateConfig No
panelClass string No
Returns : void
configureDateFieldByMode
configureDateFieldByMode(configValue: DfDateConfig)
Parameters :
Name Type Optional
configValue DfDateConfig No
Returns : void
monthSelected
monthSelected(value)
Parameters :
Name Optional
value No
Returns : void
onBlur
onBlur(event: FocusEvent)
Parameters :
Name Type Optional
event FocusEvent No
Returns : void
onInputBlur
onInputBlur()
Returns : void
startAt
startAt(field?: DfDateConfig)
Parameters :
Name Type Optional
field DfDateConfig Yes
Returns : Date | undefined
yearSelected
yearSelected(value)
Parameters :
Name Optional
value No
Returns : void

Properties

config
Type : InputSignalWithTransform<DfDateConfig | DfDateConfig>
Default value : input.required({ transform: (config: DfDateConfig) => { switch (config.value) { case DfDateDefaultValues.Today: // Changing the input config leads to a ExpressionChangedAfterItHasBeenCheckedError. So we use a copy: config = JSON.parse(JSON.stringify(config)); config.value = dayjs().format(ISO_STRING_FORMAT); break; case DfDateDefaultValues.Yesterday: config = JSON.parse(JSON.stringify(config)); config.value = dayjs().subtract(1, 'day').format(ISO_STRING_FORMAT); break; case DfDateDefaultValues.Tomorrow: config = JSON.parse(JSON.stringify(config)); config.value = dayjs().add(1, 'day').format(ISO_STRING_FORMAT); break; default: break; } return config; } })
Inherited from DfBaseComponent
Defined in DfBaseComponent:50
control
Default value : input<UntypedFormControl>(new UntypedFormControl())
Inherited from DfBaseComponent
Defined in DfBaseComponent:49
dateField
Type : NxDatefieldDirective<>
Decorators :
@ViewChild('dateField', {static: false})
datePicker
Type : NxDatepickerComponent<>
Decorators :
@ViewChild('picker')
datePickerToggle
Type : ElementRef<>
Decorators :
@ViewChild('pickerToggle', {read: ElementRef})
inputField
Type : NxInputDirective
Decorators :
@ViewChild(NxInputDirective, {static: false})
labelAlwaysFloating
Type : boolean
Optional maxDate
Type : Date
Optional minDate
Type : Date
Public parseFormatToken
Type : string
Decorators :
@Optional()
@Inject(Df_DATE_PICKER_PARSE_FORMAT)
startView
Default value : signal<DfDateStartViewTypeFullDate | DfDateStartViewTypeMonthYearOnly>( DfDateStartViewTypeFullDate.Month )
aclResource
Type : string
Inherited from DfBaseComponent
Defined in DfBaseComponent:56
componentOrControlInitFinished
Default value : new ReplaySubject<AbstractControl | undefined>(1)
Inherited from DfBaseComponent

This ReplaySubject is provided to emit the control once it is initialized.

formAclPath
Default value : input<string>()
Inherited from DfBaseComponent
Defined in DfBaseComponent:89
Readonly formEvent
Default value : output<DfEventPayload>()
Inherited from DfBaseComponent
Defined in DfBaseComponent:98

Emits when events associated to the form control happen.

The emitted object contains the data necessary to uniquely identify the event (field id and event type). It also contains the event data.

isRetailChannel
Default value : input<boolean>()
Inherited from DfBaseComponent
Defined in DfBaseComponent:91
validationConfigs
Default value : input<ValidationConfig[] | undefined>()
Inherited from DfBaseComponent
Defined in DfBaseComponent:87
import { ValidationConfig } from '@allianz/taly-core';
import { DfBaseComponent } from '@allianz/taly-core/dynamic-form';
import {
  AfterViewInit,
  Component,
  effect,
  ElementRef,
  Inject,
  InjectionToken,
  input,
  InputSignalWithTransform,
  OnInit,
  Optional,
  signal,
  ViewChild
} from '@angular/core';
import { UntypedFormControl } from '@angular/forms';
import { NxDatefieldDirective, NxDatepickerComponent } from '@aposin/ng-aquila/datefield';
import { FORMFIELD_DEFAULT_OPTIONS, FormfieldDefaultOptions } from '@aposin/ng-aquila/formfield';
import { NxInputDirective } from '@aposin/ng-aquila/input';
import dayjs from 'dayjs';
import {
  DfDateConfig,
  DfDateDefaultValues,
  DfDateStartViewTypeFullDate,
  DfDateStartViewTypeMonthYearOnly
} from './date.model';

export const Df_DATE_PICKER_PARSE_FORMAT: InjectionToken<string> = new InjectionToken<string>(
  'Df_DATE_PICKER_PARSE_FORMAT'
);

const ISO_STRING_FORMAT = 'YYYY-MM-DD';

/*
Please see the readme for a background explanation of this implementation!
*/

@Component({
  selector: 'df-date',
  styleUrls: ['./date.component.scss'],
  templateUrl: './date.component.html',
  standalone: false
})
export class DfDateComponent
  extends DfBaseComponent<DfDateConfig>
  implements OnInit, AfterViewInit
{
  override control = input<UntypedFormControl>(new UntypedFormControl());
  override config: InputSignalWithTransform<DfDateConfig, DfDateConfig> = input.required({
    transform: (config: DfDateConfig) => {
      switch (config.value) {
        case DfDateDefaultValues.Today:
          // Changing the input config leads to a ExpressionChangedAfterItHasBeenCheckedError. So we use a copy:
          config = JSON.parse(JSON.stringify(config));
          config.value = dayjs().format(ISO_STRING_FORMAT);
          break;
        case DfDateDefaultValues.Yesterday:
          config = JSON.parse(JSON.stringify(config));
          config.value = dayjs().subtract(1, 'day').format(ISO_STRING_FORMAT);
          break;
        case DfDateDefaultValues.Tomorrow:
          config = JSON.parse(JSON.stringify(config));
          config.value = dayjs().add(1, 'day').format(ISO_STRING_FORMAT);
          break;
        default:
          break;
      }
      return config;
    }
  });

  minDate?: Date;
  maxDate?: Date;
  labelAlwaysFloating!: boolean;
  startView = signal<DfDateStartViewTypeFullDate | DfDateStartViewTypeMonthYearOnly>(
    DfDateStartViewTypeFullDate.Month
  );

  @ViewChild('dateField', { static: false }) dateField!: NxDatefieldDirective<unknown>;
  @ViewChild(NxInputDirective, { static: false }) inputField!: NxInputDirective;
  @ViewChild('pickerToggle', { read: ElementRef })
  datePickerToggle!: ElementRef<unknown>;
  @ViewChild('picker') datePicker!: NxDatepickerComponent<unknown>;

  constructor(
    @Optional() @Inject(Df_DATE_PICKER_PARSE_FORMAT) public parseFormatToken: string,
    @Optional()
    @Inject(FORMFIELD_DEFAULT_OPTIONS)
    private formfieldDefaultOptions: FormfieldDefaultOptions,
    private el: ElementRef
  ) {
    super();
    effect(() => {
      const validationConfigs = this.validationConfigs();
      if (validationConfigs) {
        this.disableInvalidDates(validationConfigs);
      }
    });
  }

  override ngOnInit() {
    super.ngOnInit();

    /* If the label is not floating, then we pass the label to the nx-formfield.
     ** TODO: exchange this logic when NDBX support that with this config is supported multiple lines
     */
    this.labelAlwaysFloating = this.formfieldDefaultOptions?.nxFloatLabel === 'always';

    this.emitFormControlEventOnValueChanges();
  }

  ngAfterViewInit(): void {
    const configValue = this.config();
    // First we set the inputs that are not always there:
    if (configValue.placeholder) {
      this.inputField.placeholder = configValue.placeholder;
    }
    this.configureDateFieldByMode(configValue);
  }

  configureDateFieldByMode(configValue: DfDateConfig) {
    const monthYearOnlyFormat = 'YYYY-MM';
    const yearOnlyFormat = 'YYYY';
    switch (configValue.mode) {
      case 'monthYearOnly':
        this.dateField.parseFormat = configValue.parseFormat || monthYearOnlyFormat;
        this.dateField.displayFormat = configValue.displayFormat || monthYearOnlyFormat;
        this.addPanelClass(configValue, 'month-year-only');
        this.startView.set(configValue.startView || DfDateStartViewTypeMonthYearOnly.MultiYear);
        break;
      case 'yearOnly':
        this.dateField.parseFormat = yearOnlyFormat;
        this.dateField.displayFormat = yearOnlyFormat;
        this.addPanelClass(configValue, 'year-only');
        this.startView.set(DfDateStartViewTypeFullDate.MultiYear);
        break;
      case 'fullDate':
        this.dateField.parseFormat = configValue.parseFormat || this.parseFormatToken;
        this.dateField.displayFormat = configValue.displayFormat || this.parseFormatToken;
        if (configValue.startView) this.startView.set(configValue.startView);
        break;
    }
  }

  addPanelClass(configValue: DfDateConfig, panelClass: string) {
    if (configValue.panelClass) {
      this.datePicker.panelClass = Array.isArray(configValue.panelClass)
        ? [...configValue.panelClass, panelClass]
        : [configValue.panelClass, panelClass];
    } else {
      this.datePicker.panelClass = panelClass;
    }
  }

  onBlur(event: FocusEvent) {
    // setTimeout() needed for focused element to be updated
    setTimeout(() => {
      const dateComponentHasFocus = this.el.nativeElement.contains(event.relatedTarget);
      // Separate check for datePicker because it is opened in an overlay outside of the date component
      if (dateComponentHasFocus || this.datePicker.opened) {
        return;
      }

      this.emitFormEvent('onBlurEvent', this.control().value);
    }, 0);
  }

  onInputBlur() {
    this.control()?.markAsTouched();
  }

  yearSelected(value: unknown): void {
    if (this.config().mode === 'yearOnly') {
      this.control()?.setValue(value);
      this.datePicker.close();
    }
  }

  monthSelected(value: unknown): void {
    if (this.config().mode === 'monthYearOnly') {
      this.control()?.setValue(value);
      this.datePicker.close();
    }
  }

  startAt(field?: DfDateConfig): Date | undefined {
    const startAt = this.retrieveConfigValue(field, 'startAt');
    return startAt ? new Date(startAt) : undefined;
  }

  private retrieveConfigValue<K extends keyof DfDateConfig>(
    field: DfDateConfig | undefined,
    value: K
  ): DfDateConfig[K] | undefined {
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    return (field || ({} as any))[value];
  }

  private disableInvalidDates(validators: ValidationConfig[]) {
    validators.forEach((validator) => {
      if (validator?.minDate) {
        this.minDate = new Date(validator.minDate);
      }
      if (validator?.maxDate) {
        this.maxDate = new Date(validator.maxDate);
      }
    });
  }
}
<ng-container *aclTag="aclResource">
  <span nxCopytext class="own-label nx-font-weight-semibold" *ngIf="labelAlwaysFloating">
    {{ config().label | interpolateFromStore | async }}
    <span *ngIf="config().optionalLabel" class="nx-font-weight-regular">
      ({{ config().optionalLabel | interpolateFromStore | async }})</span
    >
  </span>
  <nx-formfield
    [label]="!labelAlwaysFloating ? (config().label | interpolateFromStore | async) : ''"
    [optionalLabel]="
      !labelAlwaysFloating ? (config().optionalLabel | interpolateFromStore | async) || '' : ''
    "
  >
    <span *ngIf="config().inputPrefix" nxFormfieldPrefix>
      {{ config().inputPrefix | interpolateFromStore | async }}
    </span>

    <input
      #inputField
      #dateField="nxDatefield"
      nxDatefield
      nxInput
      inputmode="decimal"
      [attr.data-testid]="config().testId"
      [formControl]="control()"
      [id]="config().id"
      [min]="minDate"
      [max]="maxDate"
      [datepicker]="picker"
      [strict]="!config().nonStrictParsing"
      (blur)="onInputBlur(); onBlur($event)"
      (keyup.enter)="onInputBlur()"
    />

    <nx-datepicker-toggle
      #pickerToggle
      nxFormfieldSuffix
      [for]="picker"
      *ngIf="!config().hideCalendarPicker"
      (focusout)="onBlur($event)"
    ></nx-datepicker-toggle>

    <nx-datepicker
      #picker
      [startView]="startView()"
      [startAt]="startAt(config())"
      (yearSelected)="yearSelected($event)"
      (monthSelected)="monthSelected($event)"
    ></nx-datepicker>

    <span *ngIf="config().inputSuffix" nxFormfieldSuffix>
      {{ config().inputSuffix | interpolateFromStore | async }}
    </span>

    <span *ngIf="config().hint" nxFormfieldHint>
      {{ config().hint | interpolateFromStore | async }}
    </span>

    <df-info-icon
      *ngIf="config().infoIcon"
      nxFormfieldAppendix
      [config]="config().infoIcon"
    ></df-info-icon>

    <taly-validation-errors
      nxFormfieldError
      [errorMessages]="validationConfigs()"
      [controlErrors]="control().errors"
    >
    </taly-validation-errors>

    <nx-message *ngIf="config().note" context="info" nxFormfieldNote>
      <span>{{ config().note | interpolateFromStore | async }}</span>
    </nx-message>
  </nx-formfield>
</ng-container>

./date.component.scss

:host {
  display: block;
}
/* Own label management.TODO: Remove when NDBX support multiple lines */
::ng-deep.own-label + * {
  .nx-formfield__wrapper {
    padding-top: 4px !important;
  }
}

::ng-deep.nx-calendar {
  &.month-year-only,
  &.year-only {
    .nx-calendar-change-view-button {
      display: none;
    }
  }
}
Legend
Html element
Component
Html element with directive

results matching ""

    No results matching ""