@@ -17,18 +17,28 @@ import {
17
17
AfterContentInit ,
18
18
ChangeDetectorRef ,
19
19
Self ,
20
+ ElementRef ,
20
21
} from '@angular/core' ;
21
22
import { MatFormFieldControl , MatFormField } from '@angular/material/form-field' ;
22
- import { DateRange , MAT_RANGE_DATE_SELECTION_MODEL_PROVIDER } from '@angular/material/core' ;
23
+ import {
24
+ DateRange ,
25
+ ThemePalette ,
26
+ DateAdapter ,
27
+ MatDateSelectionModel ,
28
+ } from '@angular/material/core' ;
23
29
import { NgControl , ControlContainer } from '@angular/forms' ;
24
- import { Subject } from 'rxjs' ;
30
+ import { Subject , merge } from 'rxjs' ;
25
31
import { coerceBooleanProperty , BooleanInput } from '@angular/cdk/coercion' ;
26
32
import {
27
33
MatStartDate ,
28
34
MatEndDate ,
29
35
MatDateRangeInputParent ,
30
36
MAT_DATE_RANGE_INPUT_PARENT ,
31
37
} from './date-range-input-parts' ;
38
+ import { MatDatepickerControl } from './datepicker-base' ;
39
+ import { createMissingDateImplError } from './datepicker-errors' ;
40
+ import { DateFilterFn } from './datepicker-input-base' ;
41
+ import { MatDateRangePicker } from './date-range-picker' ;
32
42
33
43
let nextUniqueId = 0 ;
34
44
@@ -49,16 +59,14 @@ let nextUniqueId = 0;
49
59
providers : [
50
60
{ provide : MatFormFieldControl , useExisting : MatDateRangeInput } ,
51
61
{ provide : MAT_DATE_RANGE_INPUT_PARENT , useExisting : MatDateRangeInput } ,
52
-
53
- // TODO(crisbeto): this will be provided by the datepicker eventually.
54
- // We provide it here for the moment so we have something to test against.
55
- MAT_RANGE_DATE_SELECTION_MODEL_PROVIDER ,
56
62
]
57
63
} )
58
64
export class MatDateRangeInput < D > implements MatFormFieldControl < DateRange < D > > ,
59
- MatDateRangeInputParent , AfterContentInit , OnDestroy {
65
+ MatDatepickerControl < D > , MatDateRangeInputParent < D > , AfterContentInit , OnDestroy {
60
66
/** Current value of the range input. */
61
- value : DateRange < D > | null = null ;
67
+ get value ( ) {
68
+ return this . _model ? this . _model . selection : null ;
69
+ }
62
70
63
71
/** Emits when the input's state has changed. */
64
72
stateChanges = new Subject < void > ( ) ;
@@ -79,11 +87,23 @@ export class MatDateRangeInput<D> implements MatFormFieldControl<DateRange<D>>,
79
87
80
88
/**
81
89
* Implemented as a part of `MatFormFieldControl`, but not used.
82
- * Use `startPlaceholder ` and `endPlaceholder` instead .
90
+ * Set the placeholder attribute on `matStartDate ` and `matEndDate` .
83
91
* @docs -private
84
92
*/
85
93
placeholder : string ;
86
94
95
+ /** The range picker that this input is associated with. */
96
+ @Input ( )
97
+ get rangePicker ( ) { return this . _rangePicker ; }
98
+ set rangePicker ( rangePicker : MatDateRangePicker < D > ) {
99
+ if ( rangePicker ) {
100
+ this . _model = rangePicker . _registerInput ( this ) ;
101
+ this . _rangePicker = rangePicker ;
102
+ this . _registerModel ( this . _model ! ) ;
103
+ }
104
+ }
105
+ private _rangePicker : MatDateRangePicker < D > ;
106
+
87
107
/** Whether the input is required. */
88
108
@Input ( )
89
109
get required ( ) : boolean { return ! ! this . _required ; }
@@ -92,6 +112,33 @@ export class MatDateRangeInput<D> implements MatFormFieldControl<DateRange<D>>,
92
112
}
93
113
private _required : boolean ;
94
114
115
+ /** Function that can be used to filter out dates within the date range picker. */
116
+ @Input ( )
117
+ get dateFilter ( ) { return this . _dateFilter ; }
118
+ set dateFilter ( value : DateFilterFn < D > ) {
119
+ this . _dateFilter = value ;
120
+ this . _revalidate ( ) ;
121
+ }
122
+ private _dateFilter : DateFilterFn < D > ;
123
+
124
+ /** The minimum valid date. */
125
+ @Input ( )
126
+ get min ( ) : D | null { return this . _min ; }
127
+ set min ( value : D | null ) {
128
+ this . _min = this . _getValidDateOrNull ( this . _dateAdapter . deserialize ( value ) ) ;
129
+ this . _revalidate ( ) ;
130
+ }
131
+ private _min : D | null ;
132
+
133
+ /** The maximum valid date. */
134
+ @Input ( )
135
+ get max ( ) : D | null { return this . _max ; }
136
+ set max ( value : D | null ) {
137
+ this . _max = this . _getValidDateOrNull ( this . _dateAdapter . deserialize ( value ) ) ;
138
+ this . _revalidate ( ) ;
139
+ }
140
+ private _max : D | null ;
141
+
95
142
/** Whether the input is disabled. */
96
143
get disabled ( ) : boolean {
97
144
if ( this . _startInput && this . _endInput ) {
@@ -123,11 +170,8 @@ export class MatDateRangeInput<D> implements MatFormFieldControl<DateRange<D>>,
123
170
/** Value for the `aria-labelledby` attribute of the inputs. */
124
171
_ariaLabelledBy : string | null = null ;
125
172
126
- /** Placeholder for the start input. */
127
- @Input ( ) startPlaceholder : string ;
128
-
129
- /** Placeholder for the end input. */
130
- @Input ( ) endPlaceholder : string ;
173
+ /** Date selection model currently registered with the input. */
174
+ private _model : MatDateSelectionModel < DateRange < D > , D > | undefined ;
131
175
132
176
/** Separator text to be shown between the inputs. */
133
177
@Input ( ) separator = '–' ;
@@ -142,14 +186,23 @@ export class MatDateRangeInput<D> implements MatFormFieldControl<DateRange<D>>,
142
186
*/
143
187
ngControl : NgControl | null ;
144
188
189
+ /** Emits when the input's disabled state changes. */
190
+ _disabledChange = new Subject < boolean > ( ) ;
191
+
145
192
constructor (
146
193
private _changeDetectorRef : ChangeDetectorRef ,
194
+ private _elementRef : ElementRef < HTMLElement > ,
147
195
@Optional ( ) @Self ( ) control : ControlContainer ,
148
- @Optional ( ) formField ?: MatFormField ) {
196
+ @Optional ( ) private _dateAdapter : DateAdapter < D > ,
197
+ @Optional ( ) private _formField ?: MatFormField ) {
198
+
199
+ if ( ! _dateAdapter ) {
200
+ throw createMissingDateImplError ( 'DateAdapter' ) ;
201
+ }
149
202
150
203
// TODO(crisbeto): remove `as any` after #18206 lands.
151
204
this . ngControl = control as any ;
152
- this . _ariaLabelledBy = formField ? formField . _labelId : null ;
205
+ this . _ariaLabelledBy = _formField ? _formField . _labelId : null ;
153
206
}
154
207
155
208
/**
@@ -179,10 +232,36 @@ export class MatDateRangeInput<D> implements MatFormFieldControl<DateRange<D>>,
179
232
if ( ! this . _endInput ) {
180
233
throw Error ( 'mat-date-range-input must contain a matEndDate input' ) ;
181
234
}
235
+
236
+ if ( this . _model ) {
237
+ this . _registerModel ( this . _model ) ;
238
+ }
239
+
240
+ // We don't need to unsubscribe from this, because we
241
+ // know that the input streams will be completed on destroy.
242
+ merge ( this . _startInput . _disabledChange , this . _endInput . _disabledChange ) . subscribe ( ( ) => {
243
+ this . _disabledChange . next ( this . disabled ) ;
244
+ } ) ;
182
245
}
183
246
184
247
ngOnDestroy ( ) {
185
248
this . stateChanges . complete ( ) ;
249
+ this . _disabledChange . unsubscribe ( ) ;
250
+ }
251
+
252
+ /** Gets the date at which the calendar should start. */
253
+ getStartValue ( ) : D | null {
254
+ return this . value ? this . value . start : null ;
255
+ }
256
+
257
+ /** Gets the input's theme palette. */
258
+ getThemePalette ( ) : ThemePalette {
259
+ return this . _formField ? this . _formField . color : undefined ;
260
+ }
261
+
262
+ /** Gets the element to which the calendar overlay should be attached. */
263
+ getConnectedOverlayOrigin ( ) : ElementRef {
264
+ return this . _formField ? this . _formField . getConnectedOverlayOrigin ( ) : this . _elementRef ;
186
265
}
187
266
188
267
/** Gets the value that is used to mirror the state input. */
@@ -200,9 +279,41 @@ export class MatDateRangeInput<D> implements MatFormFieldControl<DateRange<D>>,
200
279
this . _changeDetectorRef . markForCheck ( ) ;
201
280
}
202
281
203
- /** Opens the datepicker associated with the input. */
282
+ /** Opens the date range picker associated with the input. */
204
283
_openDatepicker ( ) {
205
- // TODO(crisbeto): implement once the datepicker is in place.
284
+ if ( this . _rangePicker ) {
285
+ this . _rangePicker . open ( ) ;
286
+ }
287
+ }
288
+
289
+ /**
290
+ * @param obj The object to check.
291
+ * @returns The given object if it is both a date instance and valid, otherwise null.
292
+ */
293
+ private _getValidDateOrNull ( obj : any ) : D | null {
294
+ return ( this . _dateAdapter . isDateInstance ( obj ) && this . _dateAdapter . isValid ( obj ) ) ? obj : null ;
295
+ }
296
+
297
+ /** Re-runs the validators on the start/end inputs. */
298
+ private _revalidate ( ) {
299
+ if ( this . _startInput ) {
300
+ this . _startInput . _validatorOnChange ( ) ;
301
+ }
302
+
303
+ if ( this . _endInput ) {
304
+ this . _endInput . _validatorOnChange ( ) ;
305
+ }
306
+ }
307
+
308
+ /** Registers the current date selection model with the start/end inputs. */
309
+ private _registerModel ( model : MatDateSelectionModel < DateRange < D > , D > ) {
310
+ if ( this . _startInput ) {
311
+ this . _startInput . _registerModel ( model ) ;
312
+ }
313
+
314
+ if ( this . _endInput ) {
315
+ this . _endInput . _registerModel ( model ) ;
316
+ }
206
317
}
207
318
208
319
static ngAcceptInputType_required : BooleanInput ;
0 commit comments