src/lib/dl-date-time-input/dl-date-time-input.directive.ts
This directive allows the user to enter dates, using the keyboard, into an input box and angular will then store a date value in the model.
The input format(s), display format, and model format are independent and fully customizable.
ControlValueAccessor
Validator
Providers |
{ provide: NG_VALUE_ACCESSOR, useExisting: DlDateTimeInputDirective, multi: true }
{ provide: NG_VALIDATORS, useExisting: DlDateTimeInputDirective, multi: true }
|
Selector | input[dlDateTimeInput] |
Methods |
Inputs |
Outputs |
HostListeners |
Accessors |
constructor(_renderer: Renderer2, _elementRef: ElementRef, _dateAdapter: DlDateAdapter<D>, _displayFormat: string, _inputFormats: string[])
|
||||||||||||||||||||||||
Constructs a new instance of this directive.
reference to the renderer.
reference to this element.
date adapter for the date type in the model.
from
Parameters :
|
dlDateTimeInputFilter | |
Type : boolean
|
|
Set a function used to determine whether or not the |
dateChange | |
Type : EventEmitter
|
|
Emits when a |
blur |
Format the input text using DL_DATE_TIME_DISPLAY_FORMAT and mark the control as touched. |
change |
Emit a |
input | ||||||
Arguments : '$event.target.value'
|
||||||
Parse the user input into a possibly valid date. The model value is not set if the input is NOT one of the DL_DATE_TIME_INPUT_FORMATS. Value of the input control.
Parameters :
|
_onBlur |
_onBlur()
|
Decorators :
@HostListener('blur')
|
Format the input text using DL_DATE_TIME_DISPLAY_FORMAT and mark the control as touched.
Returns :
void
|
_onChange |
_onChange()
|
Decorators :
@HostListener('change')
|
Emit a
Returns :
void
|
_onInput | ||||||||
_onInput(value: string | null | undefined)
|
||||||||
Decorators :
@HostListener('input', ['$event.target.value'])
|
||||||||
Parse the user input into a possibly valid date. The model value is not set if the input is NOT one of the DL_DATE_TIME_INPUT_FORMATS. Value of the input control.
Parameters :
Returns :
void
|
dlDateTimeInputFilter | ||||||||
setdlDateTimeInputFilter(inputFilterFunction: (value: D | null) => void)
|
||||||||
Set a function used to determine whether or not the
Parameters :
Returns :
void
|
value | ||||||||
getvalue()
|
||||||||
Returns
Returns :
D
|
||||||||
setvalue(newValue: D | null | undefined)
|
||||||||
Set the value of the date/time input to a value of
Parameters :
Returns :
void
|
This directive provides all of the user facing functionality to input a date and/or time using the keyboard. |
Import the module corresponding to the desired data type of the date in the model.
DlDateTimePickerDateModule
DlDateTimePickerMomentModule
DlDateTimePickerNumberModule
DlDateTimePickerStringModule
A DateAdapter
is used to adapt the data type in the model to the number
data type
used internally by the date/time picker.
If you need a different data type than what is currently supported, you can extend
DlDateAdapter<D>
to provide the data type you need then override the DlDateAdapter
provider in app.module.ts
to use your new class.
providers: [{provide: DlDateAdapter, useClass: MyCustomDateAdapter}],
The display format is defined in the DL_DATE_TIME_DISPLAY_FORMAT
token and is injected into this component
to control the display format.
DL_DATE_TIME_DISPLAY_FORMAT
defaults to moment
's lll
long date format.
Override DL_DATE_TIME_DISPLAY_FORMAT
if you do not like the default format.
{provide: DL_DATE_TIME_DISPLAY_FORMAT, useValue: '<what ever format you want goes here>'}
This directive supports multiple input formats, as defined in the DL_DATE_TIME_INPUT_FORMATS
token.
When the user inputs a string value into a text box, this directive attempts to parse the input
using one of the specified formats, in the order the format occur in the array.
Once one of the formats is able to parse the user input, the date is set in the model.
Nota bene For convenience DL_DATE_TIME_INPUT_FORMATS
defaults to support multiple formats,
which can dramatically slow down parsing performance. It can also result in successfully parsing
a date using a format that is not appropriate for your use case.
Consider overriding the DL_DATE_TIME_INPUT_FORMATS
token to only include the specific formats required by your project.
{provide: DL_DATE_TIME_INPUT_FORMATS, useValue: ['<input format zero>', ..., '<input format N>']}
See moment's parsing multiple formats page for more information on how these date formats are used.
import {Directive, ElementRef, EventEmitter, HostListener, Inject, Input, Output, Renderer2} from '@angular/core';
import {
AbstractControl,
ControlValueAccessor,
NG_VALIDATORS,
NG_VALUE_ACCESSOR,
ValidationErrors,
Validator,
ValidatorFn,
Validators,
} from '@angular/forms';
import moment from 'moment';
import {DL_DATE_TIME_DISPLAY_FORMAT, DL_DATE_TIME_INPUT_FORMATS, DlDateAdapter} from '../core/public-api';
import {DlDateTimeInputChange} from './dl-date-time-input-change';
/**
* This directive allows the user to enter dates, using the keyboard, into an input box and
* angular will then store a date value in the model.
*
* The input format(s), display format, and model format are independent and fully customizable.
*/
@Directive({
selector: 'input[dlDateTimeInput]',
providers: [
{provide: NG_VALUE_ACCESSOR, useExisting: DlDateTimeInputDirective, multi: true},
{provide: NG_VALIDATORS, useExisting: DlDateTimeInputDirective, multi: true}
]
})
export class DlDateTimeInputDirective<D> implements ControlValueAccessor, Validator {
/* tslint:disable:member-ordering */
private _filterValidator: ValidatorFn = (control: AbstractControl): ValidationErrors | null => {
return (this._inputFilter || (() => true))(this._value) ?
null : {'dlDateTimeInputFilter': {'value': control.value}};
}
private _inputFilter: (value: (D | null)) => boolean = () => true;
private _isValid = true;
private _parseValidator: ValidatorFn = (): ValidationErrors | null => {
return this._isValid ?
null : {'dlDateTimeInputParse': {'text': this._elementRef.nativeElement.value}};
}
private _changed: ((value: D) => void)[] = [];
private _touched: (() => void)[] = [];
private _validator = Validators.compose([this._parseValidator, this._filterValidator]);
private _onValidatorChange: () => void = () => {};
private _value: D | undefined = undefined;
/**
* Emits when a `change` event when date/time is selected or
* the value of the date/time picker changes.
**/
@Output()
readonly dateChange = new EventEmitter<DlDateTimeInputChange<D>>();
/**
* Constructs a new instance of this directive.
* @param _renderer
* reference to the renderer.
* @param _elementRef
* reference to this element.
* @param _dateAdapter
* date adapter for the date type in the model.
* @param _displayFormat
* from `DL_DATE_TIME_DISPLAY_FORMAT`, which defines the format to use for a valid date/time value.
* @param _inputFormats
* from `DL_DATE_TIME_INPUT_FORMATS`, which defines the input formats that allowed as valid date/time values.
* NB: moment is always in strict parse mode for this directive.
*/
constructor(
private _renderer: Renderer2,
private _elementRef: ElementRef,
private _dateAdapter: DlDateAdapter<D>,
@Inject(DL_DATE_TIME_DISPLAY_FORMAT) private readonly _displayFormat: string,
@Inject(DL_DATE_TIME_INPUT_FORMATS) private readonly _inputFormats: string[]
) {}
/**
* Set a function used to determine whether or not the `value` entered by the user is allowed.
* @param inputFilterFunction
* a function that returns `true` if the `value` entered by the user is allowed, otherwise `false`.
*/
@Input()
set dlDateTimeInputFilter(inputFilterFunction: (value: D | null) => boolean) {
this._inputFilter = inputFilterFunction || (() => true);
this._onValidatorChange();
}
/* tslint:enable:member-ordering */
/**
* Returns `D` value of the date/time input or `undefined` | `null` if no value is set.
**/
get value(): D {
return this._value;
}
/**
* Set the value of the date/time input to a value of `D` | `undefined` | `null`;
* @param newValue
* the new value of the date/time input
*/
set value(newValue: D | null | undefined) {
if (newValue !== this._value) {
this._value = newValue;
this._changed.forEach(onChanged => onChanged(this._value));
}
}
/**
* Emit a `change` event when the value of the input changes.
*/
@HostListener('change') _onChange() {
this.dateChange.emit(new DlDateTimeInputChange(this._value));
}
/**
* Format the input text using {@link DL_DATE_TIME_DISPLAY_FORMAT} and mark the control as touched.
*/
@HostListener('blur') _onBlur() {
if (this._value) {
this._setElementValue(this._value);
}
this._touched.forEach(onTouched => onTouched());
}
/**
* Parse the user input into a possibly valid date.
* The model value is not set if the input is NOT one of the {@link DL_DATE_TIME_INPUT_FORMATS}.
* @param value
* Value of the input control.
*/
@HostListener('input', ['$event.target.value']) _onInput(value: string | null | undefined): void {
const testDate = value === null || value === undefined || value === ''
? undefined
: moment(value, this._inputFormats, true);
this._isValid = testDate && testDate.isValid();
this.value = this._isValid ? this._dateAdapter.fromMilliseconds(testDate.valueOf()) : undefined;
}
/**
* @internal
*/
private _setElementValue(value: D) {
if (value !== null && value !== undefined) {
this._renderer.setProperty(this._elementRef.nativeElement, 'value', moment(value).format(this._displayFormat));
}
}
/**
* @internal
*/
registerOnChange(onChange: (value: any) => void): void {
this._changed.push(onChange);
}
/**
* @internal
*/
registerOnTouched(onTouched: () => void): void {
this._touched.push(onTouched);
}
/**
* @internal
*/
registerOnValidatorChange(validatorOnChange: () => void): void {
this._onValidatorChange = validatorOnChange;
}
/**
* @internal
*/
setDisabledState(isDisabled: boolean): void {
this._renderer.setProperty(this._elementRef.nativeElement, 'disabled', isDisabled);
}
/**
* @internal
*/
validate(control: AbstractControl): ValidationErrors | null {
return this._validator(control);
}
/**
* @internal
*/
writeValue(value: D): void {
this._isValid = true;
this.value = value;
this._setElementValue(value);
}
}