File

packages/ui/src/lib/form/period/period.component.ts

Implements

ControlValueAccessor

Metadata

providers { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => PeriodComponent), multi: true, }
selector ec-period
templateUrl ./period.component.html

Index

Properties
Methods
Inputs
Outputs

Constructor

constructor()

Inputs

disableTime
Type : FormControl
Default value : new FormControl()

If true, no time values can be selected

formControl
Type : FormControl
Default value : new FormControl()

The form control that holds the date

placeholder
Default value : ''

The input's placeholder

Outputs

changed
Type : EventEmitter<any>

Output that emits when the value changes

Methods

clear
clear()
Returns : void
isSimpleValue
isSimpleValue(value)
Parameters :
Name Optional
value No
Returns : boolean
parseValue
parseValue(value)
Parameters :
Name Optional
value No
Returns : any
registerOnChange
registerOnChange(fn)

registerOnChange implementation of ControlValueAccessor

Parameters :
Name Optional
fn No
Returns : void
registerOnTouched
registerOnTouched()

registerOnTouched implementation of ControlValueAccessor

Returns : void
setDisabledState
setDisabledState(isDisabled)
Parameters :
Name Optional
isDisabled No
Returns : void
toggleMode
toggleMode()
Returns : void
updateAmountAndPeriod
updateAmountAndPeriod(value)
Parameters :
Name Optional Default value
value No this.value
Returns : void
updateCustom
updateCustom()
Returns : void
updateSimple
updateSimple(amount, period)
Parameters :
Name Optional
amount No
period No
Returns : void
writeValue
writeValue(value: string)

Selects the given Date when the model changes.

Parameters :
Name Type Optional
value string No
Returns : void

Properties

amount
Type : number
Default value : 1
customInput
Type : any
Decorators :
@ViewChild('customInput')
customMode
Default value : false
disabled
Type : boolean

If true, the time cannot be changed

period
Type : string
Default value : 'D'
propagateChange
Default value : () => {...}

Change propagation for ControlValueAccessor

value
Type : string
Default value : ''

The current value

import { Component, ElementRef, EventEmitter, forwardRef, Input, OnInit, Output, ViewChild } from '@angular/core';
import { ControlValueAccessor, FormControl, NG_VALUE_ACCESSOR } from '@angular/forms';

@Component({
  selector: 'ec-period',
  templateUrl: './period.component.html',
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => PeriodComponent),
      multi: true,
    },
  ],
})

export class PeriodComponent implements ControlValueAccessor {
  /** Output that emits when the value changes */
  @Output() changed: EventEmitter<any> = new EventEmitter();
  /** The form control that holds the date */
  @Input() formControl: FormControl = new FormControl();
  /** If true, no time values can be selected */
  @Input() disableTime: FormControl = new FormControl();
  @ViewChild('customInput') customInput: any;
  /** The input's placeholder */
  @Input() placeholder = '';
  /** The current value */
  value = '';
  amount = 1;
  period = 'D';
  /** If true, the time cannot be changed */
  disabled: boolean;
  customMode = false;

  constructor() { }

  parseValue(value) {
    return value?.match(/^P(?:(\d+(?:\.\d+)?)(D|W|M|Y))?(?:T(\d+(?:\.\d+)?)(S|M|H))?$/)?.slice(1) || [];
  }

  // simple mode: update value when amount and period change + propagate change
  updateSimple(amount, period) {
    const isTimePeriod = period?.[0] === 'T';
    const value = 'P' + (isTimePeriod ? 'T' : '') + (amount ?? '') + (isTimePeriod ? period?.slice(1) : period ?? '');
    // console.log('change', value);
    this.value = value;
    this.propagateChange(value);
  }

  // custom mode: update amount and period when value changes + propagate change
  updateCustom() {
    setTimeout(() => {
      this.propagateChange(this.value);
    })
  }

  clear() {
    console.log('clear');
    this.value = null;
    this.period = undefined;
    this.amount = undefined;
    this.propagateChange(this.value);
  }

  // parse current value and update amount and period => should only do if 
  updateAmountAndPeriod(value = this.value) {
    if (!this.isSimpleValue(value)) {
      throw new Error('cannot update amount and period from a non simple value!')
    }
    if (!value) {
      this.amount = undefined;
      this.period = undefined;
    }
    const [dateAmount, datePeriod, timeAmount, timePeriod] = this.parseValue(value);
    this.amount = dateAmount !== undefined ? +dateAmount : +timeAmount;
    this.period = datePeriod !== undefined ? datePeriod : 'T' + timePeriod;
  }

  toggleMode() {
    if (this.customMode) {
      try {
        this.updateAmountAndPeriod(); // before switching to simple mode, update form values
      } catch (error) {
        console.log('cannot switch to simple mode when value is complex!');
        return;
      }
    }
    this.customMode = !this.customMode;
    if (this.customMode) { // after transitioning to custom mode, focus input
      setTimeout(() => {
        this.customInput?.nativeElement?.focus();
      })
    }
  }

  isSimpleValue(value) {
    const [_, datePeriod, __, timePeriod] = this.parseValue(value);
    return !value || !!timePeriod !== !!datePeriod;
  }

  /** Selects the given Date when the model changes. */
  writeValue(value: string) {
    if (!value) {
      this.amount = undefined;
      this.period = undefined;
      this.value = value;
      return;
    }
    this.value = value;
    try {
      this.updateAmountAndPeriod(value);
      this.customMode = false;
    } catch (error) {
      console.log('error', error);
      this.customMode = true;
    }
  }

  /** Change propagation for ControlValueAccessor */
  propagateChange = (_: any) => { };

  /** registerOnChange implementation of ControlValueAccessor */
  registerOnChange(fn) {
    this.propagateChange = fn;
  }

  /** registerOnTouched implementation of ControlValueAccessor */
  registerOnTouched() { }

  setDisabledState(isDisabled) {
    this.disabled = isDisabled;
  }

}
<div class="input-group">
  <input
    class="input"
    type="number"
    (ngModelChange)="updateSimple($event, period)"
    [(ngModel)]="amount"
    style="width: 150px"
    *ngIf="!customMode"
  />
  <div class="x-space-1" *ngIf="!customMode"></div>
  <select class="input" (ngModelChange)="updateSimple(amount, $event)" [(ngModel)]="period" *ngIf="!customMode">
    <option *ngIf="!disableTime" value="TS">Sekunde{{ amount > 1 ? 'n' : '' }}</option>
    <option *ngIf="!disableTime" value="TM">Minute{{ amount > 1 ? 'n' : '' }}</option>
    <option *ngIf="!disableTime" value="TH">Stunde{{ amount > 1 ? 'n' : '' }}</option>
    <option *ngIf="!disableTime" value="D">Tag{{ amount > 1 ? 'e' : '' }}</option>
    <option value="W">Woche{{ amount > 1 ? 'n' : '' }}</option>
    <option value="M">Monat{{ amount > 1 ? 'e' : '' }}</option>
    <option value="Y">Jahr{{ amount > 1 ? 'e' : '' }}</option>
  </select>
  <input
    autocomplete="off"
    class="input"
    type="text"
    (ngModelChange)="updateCustom()"
    [(ngModel)]="value"
    [class.is-hidden]="!customMode"
    [placeholder]="placeholder"
    #customInput
  />
  <a (click)="toggleMode()" class="input-group__addon btn btn_minor" [class.is-disabled]="customMode && !isSimpleValue(value)">
    <ec-icon name="code"></ec-icon>
  </a>
  <a (click)="clear()" class="input-group__addon btn btn_minor" [class.is-disabled]="!value">
    <ec-icon name="trash"></ec-icon>
  </a>
</div>
Legend
Html element
Component
Html element with directive

result-matching ""

    No results matching ""