license import { coerceBooleanProperty } from '@angular/cdk/coercion' ; import { DOWN_ARROW } from '@angular/cdk/keycodes' ; import { Directive , ElementRef , EventEmitter , forwardRef , Inject , Input , OnDestroy , Optional , Output , } from '@angular/core' ; import { AbstractControl , ControlValueAccessor , NG_VALIDATORS , NG_VALUE_ACCESSOR , ValidationErrors , Validator , ValidatorFn , Validators , } from '@angular/forms' ; import { DateAdapter , MAT_DATE_FORMATS , MatDateFormats , ThemePalette } from '@angular/material/core' ; import { MatFormField } from '@angular/material/form-field' ; import { MAT_INPUT_VALUE_ACCESSOR } from '@angular/material/input' ; import { Subscription } from 'rxjs' ; import { MatDatepicker } from './datepicker' ; import { createMissingDateImplError } from './datepicker-errors' ; docs-private export const MAT_DATEPICKER_VALUE_ACCESSOR = { provide : NG_VALUE_ACCESSOR , useExisting : forwardRef ( ( ) => MatDatepickerInput ) , multi : true } ; docs-private export const MAT_DATEPICKER_VALIDATORS = { provide : NG_VALIDATORS , useExisting : forwardRef ( ( ) => MatDatepickerInput ) , multi : true } ; /**
 * An event used for datepicker input and change events. We don't always have access to a native
 * input or change event because the event may have been triggered by the user clicking on the
 * calendar popup. For consistency, we always use MatDatepickerInputEvent instead.
 */ export class MatDatepickerInputEvent < D > { /** The new value for the target datepicker input. */ value : D | null ; constructor ( /** Reference to the datepicker input component that emitted the event. */ public target , /** Reference to the native input element associated with the datepicker input. */ public targetElement ) { this . value = this . target . value ; } } /** Directive used to connect an input to a MatDatepicker. */ @ Directive ( { selector : 'input[matDatepicker]' , providers : [ MAT_DATEPICKER_VALUE_ACCESSOR , MAT_DATEPICKER_VALIDATORS , { provide : MAT_INPUT_VALUE_ACCESSOR , useExisting : MatDatepickerInput } , ] , host : { '[attr.aria-haspopup]' : 'true' , '[attr.aria-owns]' : '(_datepicker?.opened && _datepicker.id) || null' , '[attr.min]' : 'min ? _dateAdapter.toIso8601(min) : null' , '[attr.max]' : 'max ? _dateAdapter.toIso8601(max) : null' , '[disabled]' : 'disabled' , '(input)' : '_onInput($event.target.value)' , '(change)' : '_onChange()' , '(blur)' : '_onBlur()' , '(keydown)' : '_onKeydown($event)' , } , exportAs : 'matDatepickerInput' , } ) export class MatDatepickerInput < D > implements ControlValueAccessor , OnDestroy , Validator { /** The datepicker that this input is associated with. */ @ Input ( ) set matDatepicker ( value ) { if ( ! value ) { return ; } this . _datepicker = value ; this . _datepicker . _registerInput ( this ) ; this . _datepickerSubscription . unsubscribe ( ) ; this . _datepickerSubscription = this . _datepicker . _selectedChanged . subscribe ( ( selected ) => { this . value = selected ; this . _cvaOnChange ( selected ) ; this . _onTouched ( ) ; this . dateInput . emit ( new MatDatepickerInputEvent ( this , this . _elementRef . nativeElement ) ) ; this . dateChange . emit ( new MatDatepickerInputEvent ( this , this . _elementRef . nativeElement ) ) ; } ) ; } _datepicker : MatDatepicker < D > ; /** Function that can be used to filter out dates within the datepicker. */ @ Input ( ) set matDatepickerFilter ( value ) { this . _dateFilter = value ; this . _validatorOnChange ( ) ; } _dateFilter : ( date : D | null ) => boolean ; /** The value of the input. */ @ Input ( ) get value ( ) : D | null { return this . _value ; } set value ( value ) { value = this . _dateAdapter . deserialize ( value ) ; this . _lastValueValid = ! value || this . _dateAdapter . isValid ( value ) ; value = this . _getValidDateOrNull ( value ) ; const oldDate = this . value ; this . _value = value ; this . _formatValue ( value ) ; if ( ! this . _dateAdapter . sameDate ( oldDate , value ) ) { this . _valueChange . emit ( value ) ; } } private _value : D | null ; /** The minimum valid date. */ @ Input ( ) get min ( ) : D | null { return this . _min ; } set min ( value ) { this . _min = this . _getValidDateOrNull ( this . _dateAdapter . deserialize ( value ) ) ; this . _validatorOnChange ( ) ; } private _min : D | null ; /** The maximum valid date. */ @ Input ( ) get max ( ) : D | null { return this . _max ; } set max ( value ) { this . _max = this . _getValidDateOrNull ( this . _dateAdapter . deserialize ( value ) ) ; this . _validatorOnChange ( ) ; } private _max : D | null ; /** Whether the datepicker-input is disabled. */ @ Input ( ) get disabled ( ) : boolean { return ! ! this . _disabled ; } set disabled ( value ) { const newValue = coerceBooleanProperty ( value ) ; const element = this . _elementRef . nativeElement ; if ( this . _disabled !== newValue ) { this . _disabled = newValue ; this . _disabledChange . emit ( newValue ) ; } if ( newValue && element . blur ) { element . blur ( ) ; } } private _disabled : boolean ; /** Emits when a `change` event is fired on this `<input>`. */ @ Output ( ) readonly dateChange : EventEmitter < MatDatepickerInputEvent < D > > = new EventEmitter < MatDatepickerInputEvent < D > > ( ) ; /** Emits when an `input` event is fired on this `<input>`. */ @ Output ( ) readonly dateInput : EventEmitter < MatDatepickerInputEvent < D > > = new EventEmitter < MatDatepickerInputEvent < D > > ( ) ; /** Emits when the value changes (either due to user input or programmatic change). */ _valueChange = new EventEmitter < D | null > ( ) ; /** Emits when the disabled state has changed */ _disabledChange = new EventEmitter < boolean > ( ) ; _onTouched = ( ) => { } ; private _cvaOnChange : ( value : any ) => void = ( ) => { } ; private _validatorOnChange = ( ) => { } ; private _datepickerSubscription = Subscription . EMPTY ; private _localeSubscription = Subscription . EMPTY ; /** The form control validator for whether the input parses. */ private _parseValidator : ValidatorFn = ( ) : ValidationErrors | null => { return this . _lastValueValid ? null : { 'matDatepickerParse' : { 'text' : this . _elementRef . nativeElement . value } } ; } ; /** The form control validator for the min date. */ private _minValidator : ValidatorFn = ( control ) : ValidationErrors | null => { const controlValue = this . _getValidDateOrNull ( this . _dateAdapter . deserialize ( control . value ) ) ; return ( ! this . min || ! controlValue || this . _dateAdapter . compareDate ( this . min , controlValue ) <= 0 ) ? null : { 'matDatepickerMin' : { 'min' : this . min , 'actual' : controlValue } } ; } ; /** The form control validator for the max date. */ private _maxValidator : ValidatorFn = ( control ) : ValidationErrors | null => { const controlValue = this . _getValidDateOrNull ( this . _dateAdapter . deserialize ( control . value ) ) ; return ( ! this . max || ! controlValue || this . _dateAdapter . compareDate ( this . max , controlValue ) >= 0 ) ? null : { 'matDatepickerMax' : { 'max' : this . max , 'actual' : controlValue } } ; } ; /** The form control validator for the date filter. */ private _filterValidator : ValidatorFn = ( control ) : ValidationErrors | null => { const controlValue = this . _getValidDateOrNull ( this . _dateAdapter . deserialize ( control . value ) ) ; return ! this . _dateFilter || ! controlValue || this . _dateFilter ( controlValue ) ? null : { 'matDatepickerFilter' : true } ; } ; /** The combined form control validator for this input. */ private _validator : ValidatorFn | null = Validators . compose ( [ this . _parseValidator , this . _minValidator , this . _maxValidator , this . _filterValidator ] ) ; /** Whether the last value set on the input was valid. */ private _lastValueValid = false ; constructor ( private _elementRef , @ Optional ( ) public _dateAdapter , @ Optional ( ) @ Inject ( MAT_DATE_FORMATS ) private _dateFormats , @ Optional ( ) private _formField ) { if ( ! this . _dateAdapter ) { throw createMissingDateImplError ( 'DateAdapter' ) ; } if ( ! this . _dateFormats ) { throw createMissingDateImplError ( 'MAT_DATE_FORMATS' ) ; } this . _localeSubscription = _dateAdapter . localeChanges . subscribe ( ( ) => { this . value = this . value ; } ) ; } ngOnDestroy ( ) { this . _datepickerSubscription . unsubscribe ( ) ; this . _localeSubscription . unsubscribe ( ) ; this . _valueChange . complete ( ) ; this . _disabledChange . complete ( ) ; } docs-private registerOnValidatorChange ( fn ) { this . _validatorOnChange = fn ; } docs-private validate ( c ) { return this . _validator ? this . _validator ( c ) : null ; } deprecated breaking-change getPopupConnectionElementRef ( ) { return this . getConnectedOverlayOrigin ( ) ; } return getConnectedOverlayOrigin ( ) { return this . _formField ? this . _formField . getConnectedOverlayOrigin ( ) : this . _elementRef ; } writeValue ( value ) { this . value = value ; } registerOnChange ( fn ) { this . _cvaOnChange = fn ; } registerOnTouched ( fn ) { this . _onTouched = fn ; } setDisabledState ( isDisabled ) { this . disabled = isDisabled ; } _onKeydown ( event ) { const isAltDownArrow = event . altKey && event . keyCode === DOWN_ARROW ; if ( this . _datepicker && isAltDownArrow && ! this . _elementRef . nativeElement . readOnly ) { this . _datepicker . open ( ) ; event . preventDefault ( ) ; } } _onInput ( value ) { let date = this . _dateAdapter . parse ( value , this . _dateFormats . parse . dateInput ) ; this . _lastValueValid = ! date || this . _dateAdapter . isValid ( date ) ; date = this . _getValidDateOrNull ( date ) ; if ( ! this . _dateAdapter . sameDate ( date , this . _value ) ) { this . _value = date ; this . _cvaOnChange ( date ) ; this . _valueChange . emit ( date ) ; this . dateInput . emit ( new MatDatepickerInputEvent ( this , this . _elementRef . nativeElement ) ) ; } } _onChange ( ) { this . dateChange . emit ( new MatDatepickerInputEvent ( this , this . _elementRef . nativeElement ) ) ; } /** Returns the palette used by the input's form field, if any. */ _getThemePalette ( ) { return this . _formField ? this . _formField . color : undefined ; } /** Handles blur events on the input. */ _onBlur ( ) { if ( this . value ) { this . _formatValue ( this . value ) ; } this . _onTouched ( ) ; } /** Formats a value and sets it on the input element. */ private _formatValue ( value ) { this . _elementRef . nativeElement . value = value ? this . _dateAdapter . format ( value , this . _dateFormats . display . dateInput ) : '' ; } param obj returns private _getValidDateOrNull ( obj ) { return ( this . _dateAdapter . isDateInstance ( obj ) && this . _dateAdapter . isValid ( obj ) ) ? obj : null ; } }