@@ -12,6 +12,7 @@ import {
12
12
booleanAttribute ,
13
13
ChangeDetectionStrategy ,
14
14
Component ,
15
+ computed ,
15
16
effect ,
16
17
ElementRef ,
17
18
inject ,
@@ -104,7 +105,7 @@ export class MatTimepicker<D> implements OnDestroy, MatOptionParentComponent {
104
105
private _isOpen = signal ( false ) ;
105
106
private _activeDescendant = signal < string | null > ( null ) ;
106
107
107
- private _input : MatTimepickerInput < D > ;
108
+ private _input = signal < MatTimepickerInput < D > | null > ( null ) ;
108
109
private _overlayRef : OverlayRef | null = null ;
109
110
private _portal : TemplatePortal < unknown > | null = null ;
110
111
private _optionsCacheKey : string | null = null ;
@@ -174,6 +175,9 @@ export class MatTimepicker<D> implements OnDestroy, MatOptionParentComponent {
174
175
alias : 'aria-labelledby' ,
175
176
} ) ;
176
177
178
+ /** Whether the timepicker is currently disabled. */
179
+ readonly disabled : Signal < boolean > = computed ( ( ) => ! ! this . _input ( ) ?. disabled ( ) ) ;
180
+
177
181
constructor ( ) {
178
182
if ( typeof ngDevMode === 'undefined' || ngDevMode ) {
179
183
validateAdapter ( this . _dateAdapter , this . _dateFormats ) ;
@@ -204,14 +208,16 @@ export class MatTimepicker<D> implements OnDestroy, MatOptionParentComponent {
204
208
205
209
/** Opens the timepicker. */
206
210
open ( ) : void {
207
- if ( ! this . _input ) {
211
+ const input = this . _input ( ) ;
212
+
213
+ if ( ! input ) {
208
214
return ;
209
215
}
210
216
211
217
// Focus should already be on the input, but this call is in case the timepicker is opened
212
218
// programmatically. We need to call this even if the timepicker is already open, because
213
219
// the user might be clicking the toggle.
214
- this . _input . focus ( ) ;
220
+ input . focus ( ) ;
215
221
216
222
if ( this . _isOpen ( ) ) {
217
223
return ;
@@ -220,14 +226,14 @@ export class MatTimepicker<D> implements OnDestroy, MatOptionParentComponent {
220
226
this . _isOpen . set ( true ) ;
221
227
this . _generateOptions ( ) ;
222
228
const overlayRef = this . _getOverlayRef ( ) ;
223
- overlayRef . updateSize ( { width : this . _input . getOverlayOrigin ( ) . nativeElement . offsetWidth } ) ;
229
+ overlayRef . updateSize ( { width : input . getOverlayOrigin ( ) . nativeElement . offsetWidth } ) ;
224
230
this . _portal ??= new TemplatePortal ( this . _panelTemplate ( ) , this . _viewContainerRef ) ;
225
231
overlayRef . attach ( this . _portal ) ;
226
232
this . _onOpenRender ?. destroy ( ) ;
227
233
this . _onOpenRender = afterNextRender (
228
234
( ) => {
229
235
const options = this . _options ( ) ;
230
- this . _syncSelectedState ( this . _input . value ( ) , options , options [ 0 ] ) ;
236
+ this . _syncSelectedState ( input . value ( ) , options , options [ 0 ] ) ;
231
237
this . _onOpenRender = null ;
232
238
} ,
233
239
{ injector : this . _injector } ,
@@ -247,11 +253,13 @@ export class MatTimepicker<D> implements OnDestroy, MatOptionParentComponent {
247
253
248
254
/** Registers an input with the timepicker. */
249
255
registerInput ( input : MatTimepickerInput < D > ) : void {
250
- if ( this . _input && input !== this . _input && ( typeof ngDevMode === 'undefined' || ngDevMode ) ) {
256
+ const currentInput = this . _input ( ) ;
257
+
258
+ if ( currentInput && input !== currentInput && ( typeof ngDevMode === 'undefined' || ngDevMode ) ) {
251
259
throw new Error ( 'MatTimepicker can only be registered with one input at a time' ) ;
252
260
}
253
261
254
- this . _input = input ;
262
+ this . _input . set ( input ) ;
255
263
}
256
264
257
265
ngOnDestroy ( ) : void {
@@ -265,15 +273,15 @@ export class MatTimepicker<D> implements OnDestroy, MatOptionParentComponent {
265
273
protected _selectValue ( value : D ) {
266
274
this . close ( ) ;
267
275
this . selected . emit ( { value, source : this } ) ;
268
- this . _input . focus ( ) ;
276
+ this . _input ( ) ? .focus ( ) ;
269
277
}
270
278
271
279
/** Gets the value of the `aria-labelledby` attribute. */
272
280
protected _getAriaLabelledby ( ) : string | null {
273
281
if ( this . ariaLabel ( ) ) {
274
282
return null ;
275
283
}
276
- return this . ariaLabelledby ( ) || this . _input ?. _getLabelId ( ) || null ;
284
+ return this . ariaLabelledby ( ) || this . _input ( ) ?. _getLabelId ( ) || null ;
277
285
}
278
286
279
287
/** Creates an overlay reference for the timepicker panel. */
@@ -284,7 +292,7 @@ export class MatTimepicker<D> implements OnDestroy, MatOptionParentComponent {
284
292
285
293
const positionStrategy = this . _overlay
286
294
. position ( )
287
- . flexibleConnectedTo ( this . _input . getOverlayOrigin ( ) )
295
+ . flexibleConnectedTo ( this . _input ( ) ! . getOverlayOrigin ( ) )
288
296
. withFlexibleDimensions ( false )
289
297
. withPush ( false )
290
298
. withTransformOriginOn ( '.mat-timepicker-panel' )
@@ -317,9 +325,9 @@ export class MatTimepicker<D> implements OnDestroy, MatOptionParentComponent {
317
325
318
326
this . _overlayRef . outsidePointerEvents ( ) . subscribe ( event => {
319
327
const target = _getEventTarget ( event ) as HTMLElement ;
320
- const origin = this . _input . getOverlayOrigin ( ) . nativeElement ;
328
+ const origin = this . _input ( ) ? .getOverlayOrigin ( ) . nativeElement ;
321
329
322
- if ( target && target !== origin && ! origin . contains ( target ) ) {
330
+ if ( target && origin && target !== origin && ! origin . contains ( target ) ) {
323
331
this . close ( ) ;
324
332
}
325
333
} ) ;
@@ -336,10 +344,11 @@ export class MatTimepicker<D> implements OnDestroy, MatOptionParentComponent {
336
344
if ( options !== null ) {
337
345
this . _timeOptions = options ;
338
346
} else {
347
+ const input = this . _input ( ) ;
339
348
const adapter = this . _dateAdapter ;
340
349
const timeFormat = this . _dateFormats . display . timeInput ;
341
- const min = this . _input . min ( ) || adapter . setTime ( adapter . today ( ) , 0 , 0 , 0 ) ;
342
- const max = this . _input . max ( ) || adapter . setTime ( adapter . today ( ) , 23 , 59 , 0 ) ;
350
+ const min = input ? .min ( ) || adapter . setTime ( adapter . today ( ) , 0 , 0 , 0 ) ;
351
+ const max = input ? .max ( ) || adapter . setTime ( adapter . today ( ) , 23 , 59 , 0 ) ;
343
352
const cacheKey =
344
353
interval + '/' + adapter . format ( min , timeFormat ) + '/' + adapter . format ( max , timeFormat ) ;
345
354
@@ -432,11 +441,11 @@ export class MatTimepicker<D> implements OnDestroy, MatOptionParentComponent {
432
441
*/
433
442
private _handleInputStateChanges ( ) : void {
434
443
effect ( ( ) => {
435
- const value = this . _input ?. value ( ) ;
444
+ const input = this . _input ( ) ;
436
445
const options = this . _options ( ) ;
437
446
438
- if ( this . _isOpen ( ) ) {
439
- this . _syncSelectedState ( value , options , null ) ;
447
+ if ( this . _isOpen ( ) && input ) {
448
+ this . _syncSelectedState ( input . value ( ) , options , null ) ;
440
449
}
441
450
} ) ;
442
451
}
0 commit comments