From 43ad4a509ffce83f0f5f6243eba24e4a3de83131 Mon Sep 17 00:00:00 2001 From: Felipe BF Date: Thu, 2 Apr 2026 13:00:17 -0700 Subject: [PATCH 01/11] refactor: use imject instead of constructor params in base datepicker --- .../src/lib/datepicker-base.ts | 48 ++++++++----------- 1 file changed, 21 insertions(+), 27 deletions(-) diff --git a/projects/datetime-picker/src/lib/datepicker-base.ts b/projects/datetime-picker/src/lib/datepicker-base.ts index c6796cd9..99402313 100644 --- a/projects/datetime-picker/src/lib/datepicker-base.ts +++ b/projects/datetime-picker/src/lib/datepicker-base.ts @@ -36,14 +36,12 @@ import { ElementRef, EventEmitter, HostBinding, - Inject, InjectionToken, Input, NgZone, OnChanges, OnDestroy, OnInit, - Optional, Output, SimpleChanges, ViewContainerRef, @@ -64,7 +62,6 @@ import { NgxMatCalendar, NgxMatCalendarView } from './calendar'; import { NgxMatCalendarCellClassFunction, NgxMatCalendarUserEvent } from './calendar-body'; import { NGX_MAT_DATE_RANGE_SELECTION_STRATEGY, - NgxMatDateRangeSelectionStrategy, } from './date-range-selection-strategy'; import { NgxDateRange, @@ -220,16 +217,14 @@ export class NgxMatDatepickerContent> _modelTime: D | null = null; - constructor( - private _changeDetectorRef: ChangeDetectorRef, - private _globalModel: NgxMatDateSelectionModel, - private _dateAdapter: DateAdapter, - @Optional() - @Inject(NGX_MAT_DATE_RANGE_SELECTION_STRATEGY) - private _rangeSelectionStrategy: NgxMatDateRangeSelectionStrategy, - intl: NgxMatDatepickerIntl, - ) { - this._closeButtonText = intl.closeCalendarLabel; + private _changeDetectorRef = inject(ChangeDetectorRef); + private _globalModel = inject(NgxMatDateSelectionModel); + private _dateAdapter = inject(DateAdapter); + private _rangeSelectionStrategy = inject(NGX_MAT_DATE_RANGE_SELECTION_STRATEGY, { optional: true }); + private _intl = inject(NgxMatDatepickerIntl); + + constructor() { + this._closeButtonText = this._intl.closeCalendarLabel; effect(() => { const calendar = this._calendar(); @@ -416,7 +411,6 @@ export abstract class NgxMatDatepickerBase< > implements NgxMatDatepickerPanel, OnDestroy, OnChanges { - private _scrollStrategy: () => ScrollStrategy; private _inputStateChanges = Subscription.EMPTY; private _document = inject(DOCUMENT); @@ -432,7 +426,9 @@ export abstract class NgxMatDatepickerBase< } set startAt(value: D | null) { - this._startAt = this._dateAdapter.getValidDateOrNull(this._dateAdapter.deserialize(value)); + this._startAt = (this._dateAdapter) + ? this._dateAdapter.getValidDateOrNull(this._dateAdapter.deserialize(value)) + : null; } private _startAt: D | null = null; @@ -708,20 +704,18 @@ export abstract class NgxMatDatepickerBase< /** Emits when the datepicker's state changes. */ readonly stateChanges = new Subject(); - constructor( - private _overlay: Overlay, - private _ngZone: NgZone, - private _viewContainerRef: ViewContainerRef, - @Inject(NGX_MAT_DATEPICKER_SCROLL_STRATEGY) scrollStrategy: any, - @Optional() private _dateAdapter: DateAdapter, - @Optional() private _dir: Directionality, - private _model: NgxMatDateSelectionModel, - ) { + private _overlay = inject(Overlay); + private _ngZone = inject(NgZone); + private _viewContainerRef = inject(ViewContainerRef); + private _scrollStrategy = inject(NGX_MAT_DATEPICKER_SCROLL_STRATEGY); + private _dateAdapter = inject(DateAdapter, { optional: true }); + private _dir = inject(Directionality, { optional: true }); + private _model = inject(NgxMatDateSelectionModel); + + constructor() { if (!this._dateAdapter) { throw createMissingDateImplError('DateAdapter'); } - - this._scrollStrategy = scrollStrategy; } ngOnChanges(changes: SimpleChanges) { @@ -915,7 +909,7 @@ export abstract class NgxMatDatepickerBase< isDialog ? 'cdk-overlay-dark-backdrop' : 'mat-overlay-transparent-backdrop', this._backdropHarnessClass, ], - direction: this._dir, + direction: this._dir ?? "ltr", scrollStrategy: isDialog ? this._overlay.scrollStrategies.block() : this._scrollStrategy(), panelClass: `mat-datepicker-${isDialog ? 'dialog' : 'popup'}`, }), From 05aac337c161828531afd88f3c5143771033d8f4 Mon Sep 17 00:00:00 2001 From: Felipe BF Date: Thu, 2 Apr 2026 14:22:28 -0700 Subject: [PATCH 02/11] refactor: use inject calls for datepicker input components --- .../src/lib/date-range-input-parts.ts | 100 +++++------------- .../src/lib/datepicker-input-base.ts | 53 +++++++--- .../src/lib/datepicker-input.ts | 33 +++--- 3 files changed, 80 insertions(+), 106 deletions(-) diff --git a/projects/datetime-picker/src/lib/date-range-input-parts.ts b/projects/datetime-picker/src/lib/date-range-input-parts.ts index bcab6895..f3a2c3de 100644 --- a/projects/datetime-picker/src/lib/date-range-input-parts.ts +++ b/projects/datetime-picker/src/lib/date-range-input-parts.ts @@ -4,13 +4,11 @@ import { Directive, DoCheck, ElementRef, - Inject, inject, InjectionToken, Injector, Input, OnInit, - Optional, } from '@angular/core'; import { AbstractControl, @@ -24,10 +22,7 @@ import { Validators, } from '@angular/forms'; import { - DateAdapter, ErrorStateMatcher, - MAT_DATE_FORMATS, - MatDateFormats, } from '@angular/material/core'; import { _computeAriaAccessibleName } from './aria-accessible-name'; import { NgxDateRange, NgxDateSelectionModelChange } from './date-selection-model'; @@ -55,7 +50,7 @@ export interface NgxMatDateRangeInputParent { * to the parts without circular dependencies. */ export const NGX_MAT_DATE_RANGE_INPUT_PARENT = new InjectionToken< - NgxMatDateRangeInputParent + NgxMatDateRangeInputParent >('NGX_MAT_DATE_RANGE_INPUT_PARENT'); /** @@ -82,19 +77,12 @@ abstract class NgxMatDateRangeInputPartBase protected readonly _dir = inject(Directionality, { optional: true }); - constructor( - @Inject(NGX_MAT_DATE_RANGE_INPUT_PARENT) - public _rangeInput: NgxMatDateRangeInputParent, - public override _elementRef: ElementRef, - public _defaultErrorStateMatcher: ErrorStateMatcher, - private _injector: Injector, - @Optional() public _parentForm: NgForm, - @Optional() public _parentFormGroup: FormGroupDirective, - @Optional() dateAdapter: DateAdapter, - @Optional() @Inject(MAT_DATE_FORMATS) dateFormats: MatDateFormats, - ) { - super(_elementRef, dateAdapter, dateFormats); - } + public override _elementRef = inject(ElementRef); + public _rangeInput = inject(NGX_MAT_DATE_RANGE_INPUT_PARENT); + public _defaultErrorStateMatcher = inject(ErrorStateMatcher); + protected _injector = inject(Injector); + public _parentForm = inject(NgForm, { optional: true }); + public _parentFormGroup = inject(FormGroupDirective, { optional: true }); ngOnInit() { // We need the date input to provide itself as a `ControlValueAccessor` and a `Validator`, while @@ -216,8 +204,8 @@ abstract class NgxMatDateRangeInputPartBase '(keydown)': '_onKeydown($event)', '[attr.aria-haspopup]': '_rangeInput.rangePicker ? "dialog" : null', '[attr.aria-owns]': '(_rangeInput.rangePicker?.opened && _rangeInput.rangePicker.id) || null', - '[attr.min]': '_getMinDate() ? _dateAdapter.toIso8601(_getMinDate()!) : null', - '[attr.max]': '_getMaxDate() ? _dateAdapter.toIso8601(_getMaxDate()!) : null', + '[attr.min]': '_dateAdapter && _getMinDate() ? _dateAdapter.toIso8601(_getMinDate()!) : null', + '[attr.max]': '_dateAdapter && _getMaxDate() ? _dateAdapter.toIso8601(_getMaxDate()!) : null', '(blur)': '_onBlur()', type: 'text', }, @@ -233,6 +221,9 @@ abstract class NgxMatDateRangeInputPartBase export class NgxMatStartDate extends NgxMatDateRangeInputPartBase { /** Validator that checks that the start date isn't after the end date. */ private _startValidator: ValidatorFn = (control: AbstractControl): ValidationErrors | null => { + if (!this._dateAdapter) + return null; + const start = this._dateAdapter.getValidDateOrNull( this._dateAdapter.deserialize(control.value), ); @@ -242,29 +233,6 @@ export class NgxMatStartDate extends NgxMatDateRangeInputPartBase { : { matStartDateInvalid: { end: end, actual: start } }; }; - constructor( - @Inject(NGX_MAT_DATE_RANGE_INPUT_PARENT) - rangeInput: NgxMatDateRangeInputParent, - elementRef: ElementRef, - defaultErrorStateMatcher: ErrorStateMatcher, - injector: Injector, - @Optional() parentForm: NgForm, - @Optional() parentFormGroup: FormGroupDirective, - @Optional() dateAdapter: DateAdapter, - @Optional() @Inject(MAT_DATE_FORMATS) dateFormats: MatDateFormats, - ) { - super( - rangeInput, - elementRef, - defaultErrorStateMatcher, - injector, - parentForm, - parentFormGroup, - dateAdapter, - dateFormats, - ); - } - protected _validator = Validators.compose([...super._getValidators(), this._startValidator]); protected _getValueFromModel(modelValue: NgxDateRange | null) { @@ -277,10 +245,13 @@ export class NgxMatStartDate extends NgxMatDateRangeInputPartBase { if (!super._shouldHandleChangeEvent(change)) { return false; } else { + const fnCompare: (ch: NgxDateSelectionModelChange>) => boolean = (this._dateAdapter) + ? ch => !!this._dateAdapter!.compareDate(ch.oldValue!.start, ch.selection!.start) + : _ => false; + return !change.oldValue?.start ? !!change.selection!.start - : !change.selection!.start || - !!this._dateAdapter.compareDate(change.oldValue.start, change.selection!.start); + : !change.selection!.start || fnCompare(change); } } @@ -330,8 +301,8 @@ export class NgxMatStartDate extends NgxMatDateRangeInputPartBase { '(keydown)': '_onKeydown($event)', '[attr.aria-haspopup]': '_rangeInput.rangePicker ? "dialog" : null', '[attr.aria-owns]': '(_rangeInput.rangePicker?.opened && _rangeInput.rangePicker.id) || null', - '[attr.min]': '_getMinDate() ? _dateAdapter.toIso8601(_getMinDate()!) : null', - '[attr.max]': '_getMaxDate() ? _dateAdapter.toIso8601(_getMaxDate()!) : null', + '[attr.min]': '_dateAdapter && _getMinDate() ? _dateAdapter.toIso8601(_getMinDate()!) : null', + '[attr.max]': '_dateAdapter && _getMaxDate() ? _dateAdapter.toIso8601(_getMaxDate()!) : null', '(blur)': '_onBlur()', type: 'text', }, @@ -347,6 +318,9 @@ export class NgxMatStartDate extends NgxMatDateRangeInputPartBase { export class NgxMatEndDate extends NgxMatDateRangeInputPartBase { /** Validator that checks that the end date isn't before the start date. */ private _endValidator: ValidatorFn = (control: AbstractControl): ValidationErrors | null => { + if (!this._dateAdapter) + return null; + const end = this._dateAdapter.getValidDateOrNull(this._dateAdapter.deserialize(control.value)); const start = this._model ? this._model.selection.start : null; return !end || !start || this._dateAdapter.compareDate(end, start) >= 0 @@ -354,29 +328,6 @@ export class NgxMatEndDate extends NgxMatDateRangeInputPartBase { : { matEndDateInvalid: { start: start, actual: end } }; }; - constructor( - @Inject(NGX_MAT_DATE_RANGE_INPUT_PARENT) - rangeInput: NgxMatDateRangeInputParent, - elementRef: ElementRef, - defaultErrorStateMatcher: ErrorStateMatcher, - injector: Injector, - @Optional() parentForm: NgForm, - @Optional() parentFormGroup: FormGroupDirective, - @Optional() dateAdapter: DateAdapter, - @Optional() @Inject(MAT_DATE_FORMATS) dateFormats: MatDateFormats, - ) { - super( - rangeInput, - elementRef, - defaultErrorStateMatcher, - injector, - parentForm, - parentFormGroup, - dateAdapter, - dateFormats, - ); - } - protected _validator = Validators.compose([...super._getValidators(), this._endValidator]); protected _getValueFromModel(modelValue: NgxDateRange | null) { @@ -389,10 +340,13 @@ export class NgxMatEndDate extends NgxMatDateRangeInputPartBase { if (!super._shouldHandleChangeEvent(change)) { return false; } else { + const fnCompare: (ch: NgxDateSelectionModelChange>) => boolean = (this._dateAdapter) + ? ch => !!this._dateAdapter!.compareDate(ch.oldValue!.end, ch.selection!.end) + : _ => false; + return !change.oldValue?.end ? !!change.selection!.end - : !change.selection!.end || - !!this._dateAdapter.compareDate(change.oldValue.end, change.selection!.end); + : !change.selection!.end || fnCompare(change); } } diff --git a/projects/datetime-picker/src/lib/datepicker-input-base.ts b/projects/datetime-picker/src/lib/datepicker-input-base.ts index e0ba54fb..5bcf24fc 100644 --- a/projects/datetime-picker/src/lib/datepicker-input-base.ts +++ b/projects/datetime-picker/src/lib/datepicker-input-base.ts @@ -5,12 +5,11 @@ import { AfterViewInit, Directive, ElementRef, - Inject, Input, OnChanges, OnDestroy, - Optional, SimpleChanges, + inject, output, } from '@angular/core'; import { @@ -23,7 +22,6 @@ import { import { DateAdapter, MAT_DATE_FORMATS, - MatDateFormats, ThemePalette, } from '@angular/material/core'; import { Subject, Subscription } from 'rxjs'; @@ -147,6 +145,9 @@ export abstract class NgxMatDatepickerInputBase { + if (!this._dateAdapter) + return null; + const controlValue = this._dateAdapter.getValidDateOrNull( this._dateAdapter.deserialize(control.value), ); @@ -157,6 +158,9 @@ export abstract class NgxMatDatepickerInputBase { + if (!this._dateAdapter) + return null; + const controlValue = this._dateAdapter.getValidDateOrNull( this._dateAdapter.deserialize(control.value), ); @@ -168,6 +172,9 @@ export abstract class NgxMatDatepickerInputBase { + if (!this._dateAdapter) + return null; + const controlValue = this._dateAdapter.getValidDateOrNull( this._dateAdapter.deserialize(control.value), ); @@ -231,13 +238,11 @@ export abstract class NgxMatDatepickerInputBase, - @Optional() public _dateAdapter: DateAdapter, - @Optional() - @Inject(MAT_DATE_FORMATS) - private _dateFormats: MatDateFormats, - ) { + protected _elementRef = inject(ElementRef); + public _dateAdapter = inject(DateAdapter, { optional: true }); + protected _dateFormats = inject(MAT_DATE_FORMATS, { optional: true }); + + constructor() { if (!this._dateAdapter) { throw createMissingDateImplError('DateAdapter'); } @@ -246,7 +251,7 @@ export abstract class NgxMatDatepickerInputBase { + this._localeSubscription = this._dateAdapter.localeChanges.subscribe(() => { this._assignValueProgrammatically(this.value); }); } @@ -256,7 +261,7 @@ export abstract class NgxMatDatepickerInputBase return this._min; } set min(value: D | null) { + if (!this._dateAdapter) { + this._min = null; + return; + } + const validValue = this._dateAdapter.getValidDateOrNull(this._dateAdapter.deserialize(value)); if (!this._dateAdapter.sameDate(validValue, this._min)) { @@ -104,6 +105,11 @@ export class NgxMatDatepickerInput return this._max; } set max(value: D | null) { + if (!this._dateAdapter) { + this._max = null; + return; + } + const validValue = this._dateAdapter.getValidDateOrNull(this._dateAdapter.deserialize(value)); if (!this._dateAdapter.sameDate(validValue, this._max)) { @@ -131,15 +137,10 @@ export class NgxMatDatepickerInput /** The combined form control validator for this input. */ protected _validator: ValidatorFn | null; - constructor( - elementRef: ElementRef, - @Optional() dateAdapter: DateAdapter, - @Optional() @Inject(MAT_DATE_FORMATS) dateFormats: MatDateFormats, - @Optional() - @Inject(MAT_FORM_FIELD) - private _formField?: _NgxMatFormFieldPartial, - ) { - super(elementRef, dateAdapter, dateFormats); + private _formField = inject(MAT_FORM_FIELD, { optional: true }); + + constructor() { + super(); this._validator = Validators.compose(super._getValidators()); } From 682d06c2668a1f34a91f4da4e992ccf5b574e22f Mon Sep 17 00:00:00 2001 From: Felipe BF Date: Thu, 2 Apr 2026 14:33:09 -0700 Subject: [PATCH 03/11] refactor: use inject calls for datepicker input component --- .../src/lib/date-range-input.ts | 40 +++++++++++-------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/projects/datetime-picker/src/lib/date-range-input.ts b/projects/datetime-picker/src/lib/date-range-input.ts index c848b45f..264d511b 100644 --- a/projects/datetime-picker/src/lib/date-range-input.ts +++ b/projects/datetime-picker/src/lib/date-range-input.ts @@ -7,12 +7,10 @@ import { Component, ContentChild, ElementRef, - Inject, + inject, Input, OnChanges, OnDestroy, - Optional, - Self, SimpleChanges, ViewEncapsulation, } from '@angular/core'; @@ -173,6 +171,11 @@ export class NgxMatDateRangeInput return this._min; } set min(value: D | null) { + if (!this._dateAdapter) { + this._min = null; + return; + } + const validValue = this._dateAdapter.getValidDateOrNull(this._dateAdapter.deserialize(value)); if (!this._dateAdapter.sameDate(validValue, this._min)) { @@ -188,6 +191,11 @@ export class NgxMatDateRangeInput return this._max; } set max(value: D | null) { + if (!this._dateAdapter) { + this._max = null; + return; + } + const validValue = this._dateAdapter.getValidDateOrNull(this._dateAdapter.deserialize(value)); if (!this._dateAdapter.sameDate(validValue, this._max)) { @@ -257,23 +265,21 @@ export class NgxMatDateRangeInput /** Emits when the input's state has changed. */ readonly stateChanges = new Subject(); - constructor( - private _changeDetectorRef: ChangeDetectorRef, - private _elementRef: ElementRef, - @Optional() @Self() control: ControlContainer, - @Optional() private _dateAdapter: DateAdapter, - @Optional() - @Inject(MAT_FORM_FIELD) - private _formField?: _NgxMatFormFieldPartial, - ) { - if (!_dateAdapter) { + private _changeDetectorRef = inject(ChangeDetectorRef); + private _elementRef = inject(ElementRef); + control = inject(ControlContainer, { optional: true, self: true }); + private _dateAdapter = inject(DateAdapter, { optional: true }); + private _formField = inject(MAT_FORM_FIELD, { optional: true }); + + constructor() { + if (!this._dateAdapter) { throw createMissingDateImplError('DateAdapter'); } // The datepicker module can be used both with MDC and non-MDC form fields. We have // to conditionally add the MDC input class so that the range picker looks correctly. - if (_formField?._elementRef.nativeElement.classList.contains('mat-mdc-form-field')) { - _elementRef.nativeElement.classList.add( + if (this._formField?._elementRef.nativeElement.classList.contains('mat-mdc-form-field')) { + this._elementRef.nativeElement.classList.add( 'mat-mdc-input-element', 'mat-mdc-form-field-input-control', 'mdc-text-field__input', @@ -281,7 +287,7 @@ export class NgxMatDateRangeInput } // TODO(crisbeto): remove `as any` after #18206 lands. - this.ngControl = control as any; + this.ngControl = this.control as any; } /** @@ -327,7 +333,7 @@ export class NgxMatDateRangeInput } ngOnChanges(changes: SimpleChanges) { - if (dateInputsHaveChanged(changes, this._dateAdapter)) { + if (this._dateAdapter && dateInputsHaveChanged(changes, this._dateAdapter)) { this.stateChanges.next(undefined); } } From 595fd12935d4b0d9967c7a835f583ffbc84d4139 Mon Sep 17 00:00:00 2001 From: Felipe BF Date: Thu, 2 Apr 2026 14:48:35 -0700 Subject: [PATCH 04/11] refactor: use inject calls for month view --- .../datetime-picker/src/lib/month-view.html | 2 +- .../datetime-picker/src/lib/month-view.ts | 99 ++++++++++++------- 2 files changed, 64 insertions(+), 37 deletions(-) diff --git a/projects/datetime-picker/src/lib/month-view.html b/projects/datetime-picker/src/lib/month-view.html index a17270fa..65bbc198 100644 --- a/projects/datetime-picker/src/lib/month-view.html +++ b/projects/datetime-picker/src/lib/month-view.html @@ -29,7 +29,7 @@ [previewEnd]="_previewEnd" [isRange]="_isRange" [labelMinRequiredCells]="3" - [activeCell]="_dateAdapter.getDate(activeDate) - 1" + [activeCell]="_dateAdapter ? _dateAdapter.getDate(activeDate) - 1 : 0" [startDateAccessibleName]="startDateAccessibleName()" [endDateAccessibleName]="endDateAccessibleName()" (selectedValueChange)="_dateSelected($event)" diff --git a/projects/datetime-picker/src/lib/month-view.ts b/projects/datetime-picker/src/lib/month-view.ts index c727ba55..805ba69e 100644 --- a/projects/datetime-picker/src/lib/month-view.ts +++ b/projects/datetime-picker/src/lib/month-view.ts @@ -18,18 +18,17 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, - Inject, Input, OnChanges, OnDestroy, - Optional, SimpleChanges, ViewEncapsulation, + inject, input, output, viewChild, } from '@angular/core'; -import { DateAdapter, MAT_DATE_FORMATS, MatDateFormats } from '@angular/material/core'; +import { DateAdapter, MAT_DATE_FORMATS } from '@angular/material/core'; import { Subscription } from 'rxjs'; import { startWith } from 'rxjs/operators'; import { @@ -40,7 +39,6 @@ import { } from './calendar-body'; import { NGX_MAT_DATE_RANGE_SELECTION_STRATEGY, - NgxMatDateRangeSelectionStrategy, } from './date-range-selection-strategy'; import { NgxDateRange } from './date-selection-model'; import { createMissingDateImplError } from './datepicker-errors'; @@ -73,6 +71,9 @@ export class NgxMatMonthView implements AfterContentInit, OnChanges, OnDestro return this._activeDate; } set activeDate(value: D) { + if (!this._dateAdapter) + return; + const oldActiveDate = this._activeDate; const validDate = this._dateAdapter.getValidDateOrNull(this._dateAdapter.deserialize(value)) || @@ -90,7 +91,7 @@ export class NgxMatMonthView implements AfterContentInit, OnChanges, OnDestro return this._selected; } set selected(value: NgxDateRange | D | null) { - if (value instanceof NgxDateRange) { + if (!this._dateAdapter || value instanceof NgxDateRange) { this._selected = value; } else { this._selected = this._dateAdapter.getValidDateOrNull(this._dateAdapter.deserialize(value)); @@ -106,6 +107,11 @@ export class NgxMatMonthView implements AfterContentInit, OnChanges, OnDestro return this._minDate; } set minDate(value: D | null) { + if (!this._dateAdapter) { + this._minDate = null; + return; + } + this._minDate = this._dateAdapter.getValidDateOrNull(this._dateAdapter.deserialize(value)); } private _minDate: D | null = null; @@ -116,6 +122,11 @@ export class NgxMatMonthView implements AfterContentInit, OnChanges, OnDestro return this._maxDate; } set maxDate(value: D | null) { + if (!this._dateAdapter) { + this._maxDate = null; + return; + } + this._maxDate = this._dateAdapter.getValidDateOrNull(this._dateAdapter.deserialize(value)); } private _maxDate: D | null = null; @@ -198,17 +209,13 @@ export class NgxMatMonthView implements AfterContentInit, OnChanges, OnDestro /** The names of the weekdays. */ _weekdays: { long: string; narrow: string }[] | null = null; - constructor( - readonly _changeDetectorRef: ChangeDetectorRef, - @Optional() - @Inject(MAT_DATE_FORMATS) - private _dateFormats: MatDateFormats, - @Optional() public _dateAdapter: DateAdapter, - @Optional() private _dir?: Directionality, - @Inject(NGX_MAT_DATE_RANGE_SELECTION_STRATEGY) - @Optional() - private _rangeStrategy?: NgxMatDateRangeSelectionStrategy, - ) { + readonly _changeDetectorRef = inject(ChangeDetectorRef); + private _dateFormats = inject(MAT_DATE_FORMATS, { optional: true }); + public _dateAdapter = inject(DateAdapter, { optional: true }); + private _dir = inject(Directionality, { optional: true }); + private _rangeStrategy = inject(NGX_MAT_DATE_RANGE_SELECTION_STRATEGY, { optional: true }); + + constructor() { if (!this._dateAdapter) { throw createMissingDateImplError('DateAdapter'); } @@ -220,6 +227,9 @@ export class NgxMatMonthView implements AfterContentInit, OnChanges, OnDestro } ngAfterContentInit() { + if (!this._dateAdapter) + return; + this._rerenderSubscription = this._dateAdapter.localeChanges .pipe(startWith(null)) .subscribe(() => this._init()); @@ -277,9 +287,10 @@ export class NgxMatMonthView implements AfterContentInit, OnChanges, OnDestro _updateActiveDate(event: NgxMatCalendarUserEvent) { const month = event.value; const oldActiveDate = this._activeDate; - this.activeDate = this._getDateFromDayOfMonth(month); + const dateToAssign = this._getDateFromDayOfMonth(month); - if (this._dateAdapter.compareDate(oldActiveDate, this.activeDate)) { + if (this._dateAdapter && dateToAssign && this._dateAdapter.compareDate(oldActiveDate, dateToAssign)) { + this.activeDate = dateToAssign; this.activeDateChange.emit(this._activeDate); } } @@ -295,25 +306,25 @@ export class NgxMatMonthView implements AfterContentInit, OnChanges, OnDestro switch (event.keyCode) { case LEFT_ARROW: - this.activeDate = this._dateAdapter.addCalendarDays(this._activeDate, isRtl ? 1 : -1); + this.activeDate = this._dateAdapter?.addCalendarDays(this._activeDate, isRtl ? 1 : -1); break; case RIGHT_ARROW: - this.activeDate = this._dateAdapter.addCalendarDays(this._activeDate, isRtl ? -1 : 1); + this.activeDate = this._dateAdapter?.addCalendarDays(this._activeDate, isRtl ? -1 : 1); break; case UP_ARROW: - this.activeDate = this._dateAdapter.addCalendarDays(this._activeDate, -7); + this.activeDate = this._dateAdapter?.addCalendarDays(this._activeDate, -7); break; case DOWN_ARROW: - this.activeDate = this._dateAdapter.addCalendarDays(this._activeDate, 7); + this.activeDate = this._dateAdapter?.addCalendarDays(this._activeDate, 7); break; case HOME: - this.activeDate = this._dateAdapter.addCalendarDays( + this.activeDate = this._dateAdapter?.addCalendarDays( this._activeDate, 1 - this._dateAdapter.getDate(this._activeDate), ); break; case END: - this.activeDate = this._dateAdapter.addCalendarDays( + this.activeDate = this._dateAdapter?.addCalendarDays( this._activeDate, this._dateAdapter.getNumDaysInMonth(this._activeDate) - this._dateAdapter.getDate(this._activeDate), @@ -321,13 +332,13 @@ export class NgxMatMonthView implements AfterContentInit, OnChanges, OnDestro break; case PAGE_UP: this.activeDate = event.altKey - ? this._dateAdapter.addCalendarYears(this._activeDate, -1) - : this._dateAdapter.addCalendarMonths(this._activeDate, -1); + ? this._dateAdapter?.addCalendarYears(this._activeDate, -1) + : this._dateAdapter?.addCalendarMonths(this._activeDate, -1); break; case PAGE_DOWN: this.activeDate = event.altKey - ? this._dateAdapter.addCalendarYears(this._activeDate, 1) - : this._dateAdapter.addCalendarMonths(this._activeDate, 1); + ? this._dateAdapter?.addCalendarYears(this._activeDate, 1) + : this._dateAdapter?.addCalendarMonths(this._activeDate, 1); break; case ENTER: case SPACE: @@ -363,7 +374,7 @@ export class NgxMatMonthView implements AfterContentInit, OnChanges, OnDestro return; } - if (this._dateAdapter.compareDate(oldActiveDate, this.activeDate)) { + if (this._dateAdapter && this._dateAdapter.compareDate(oldActiveDate, this.activeDate)) { this.activeDateChange.emit(this.activeDate); this._focusActiveCellAfterViewChecked(); @@ -376,7 +387,7 @@ export class NgxMatMonthView implements AfterContentInit, OnChanges, OnDestro /** Handles keyup events on the calendar body when calendar is in month view. */ _handleCalendarBodyKeyup(event: KeyboardEvent): void { if (event.keyCode === SPACE || event.keyCode === ENTER) { - if (this._selectionKeyPressed && this._canSelect(this._activeDate)) { + if (this._dateAdapter && this._selectionKeyPressed && this._canSelect(this._activeDate)) { this._dateSelected({ value: this._dateAdapter.getDate(this._activeDate), event, @@ -389,13 +400,14 @@ export class NgxMatMonthView implements AfterContentInit, OnChanges, OnDestro /** Initializes this month view. */ _init() { + if (!this._dateAdapter || !this._dateFormats) + return; + this._setRanges(this.selected); this._todayDate = this._getCellCompareValue(this._dateAdapter.today()); this._monthLabel = this._dateFormats.display.monthLabel ? this._dateAdapter.format(this.activeDate, this._dateFormats.display.monthLabel) - : this._dateAdapter - .getMonthNames('short') - [this._dateAdapter.getMonth(this.activeDate)].toLocaleUpperCase(); + : this._dateAdapter.getMonthNames('short')[this._dateAdapter.getMonth(this.activeDate)].toLocaleUpperCase(); const firstOfMonth = this._dateAdapter.createDate( this._dateAdapter.getYear(this.activeDate), @@ -488,7 +500,10 @@ export class NgxMatMonthView implements AfterContentInit, OnChanges, OnDestro * Takes a day of the month and returns a new date in the same month and year as the currently * active date. The returned date will have the same day of the month as the argument date. */ - private _getDateFromDayOfMonth(dayOfMonth: number): D { + private _getDateFromDayOfMonth(dayOfMonth: number): D | null { + if (!this._dateAdapter) + return null; + return this._dateAdapter.createDate( this._dateAdapter.getYear(this.activeDate), this._dateAdapter.getMonth(this.activeDate), @@ -498,6 +513,9 @@ export class NgxMatMonthView implements AfterContentInit, OnChanges, OnDestro /** Initializes the weekdays. */ private _initWeekdays() { + if (!this._dateAdapter) + return; + const firstDayOfWeek = this._dateAdapter.getFirstDayOfWeek(); const narrowWeekdays = this._dateAdapter.getDayOfWeekNames('narrow'); const longWeekdays = this._dateAdapter.getDayOfWeekNames('long'); @@ -511,6 +529,9 @@ export class NgxMatMonthView implements AfterContentInit, OnChanges, OnDestro /** Creates MatCalendarCells for the dates in this month. */ private _createWeekCells() { + if (!this._dateAdapter || !this._dateFormats) + return; + const daysInMonth = this._dateAdapter.getNumDaysInMonth(this.activeDate); const dateNames = this._dateAdapter.getDateNames(); this._weeks = [[]]; @@ -544,6 +565,9 @@ export class NgxMatMonthView implements AfterContentInit, OnChanges, OnDestro /** Date filter for the month */ private _shouldEnableDate(date: D): boolean { + if (!this._dateAdapter) + return false; + return ( !!date && (!this.minDate || this._dateAdapter.compareDate(date, this.minDate) >= 0) && @@ -557,13 +581,16 @@ export class NgxMatMonthView implements AfterContentInit, OnChanges, OnDestro * Returns null if the given Date is in another month. */ private _getDateInCurrentMonth(date: D | null): number | null { - return date && this._hasSameMonthAndYear(date, this.activeDate) + return this._dateAdapter && date && this._hasSameMonthAndYear(date, this.activeDate) ? this._dateAdapter.getDate(date) : null; } /** Checks whether the 2 dates are non-null and fall within the same month of the same year. */ private _hasSameMonthAndYear(d1: D | null, d2: D | null): boolean { + if (!this._dateAdapter) + return false; + return !!( d1 && d2 && @@ -574,7 +601,7 @@ export class NgxMatMonthView implements AfterContentInit, OnChanges, OnDestro /** Gets the value that will be used to one cell to another. */ private _getCellCompareValue(date: D | undefined | null): number | null { - if (date) { + if (this._dateAdapter && date) { // We use the time since the Unix epoch to compare dates in this view, rather than the // cell values, because we need to support ranges that span across multiple months/years. const year = this._dateAdapter.getYear(date); From 2a5e01a18c02e75a1ce72ee1f4c4f4b0852a98ae Mon Sep 17 00:00:00 2001 From: Felipe BF Date: Thu, 2 Apr 2026 15:56:40 -0700 Subject: [PATCH 05/11] refactor: use inject calls for year view --- .../datetime-picker/src/lib/year-view.html | 2 +- projects/datetime-picker/src/lib/year-view.ts | 104 ++++++++++++------ 2 files changed, 69 insertions(+), 37 deletions(-) diff --git a/projects/datetime-picker/src/lib/year-view.html b/projects/datetime-picker/src/lib/year-view.html index 6311cc23..ecdf5e81 100644 --- a/projects/datetime-picker/src/lib/year-view.html +++ b/projects/datetime-picker/src/lib/year-view.html @@ -14,7 +14,7 @@ [labelMinRequiredCells]="2" [numCols]="4" [cellAspectRatio]="4 / 7" - [activeCell]="_dateAdapter.getMonth(activeDate)" + [activeCell]="_dateAdapter ? _dateAdapter.getMonth(activeDate) : 0" (selectedValueChange)="_monthSelected($event)" (activeDateChange)="_updateActiveDate($event)" (keyup)="_handleCalendarBodyKeyup($event)" diff --git a/projects/datetime-picker/src/lib/year-view.ts b/projects/datetime-picker/src/lib/year-view.ts index de4cd5d0..5381b0cb 100644 --- a/projects/datetime-picker/src/lib/year-view.ts +++ b/projects/datetime-picker/src/lib/year-view.ts @@ -16,16 +16,15 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, - Inject, Input, OnDestroy, - Optional, ViewEncapsulation, + inject, input, output, viewChild, } from '@angular/core'; -import { DateAdapter, MAT_DATE_FORMATS, MatDateFormats } from '@angular/material/core'; +import { DateAdapter, MAT_DATE_FORMATS } from '@angular/material/core'; import { Subscription } from 'rxjs'; import { startWith } from 'rxjs/operators'; import { @@ -61,6 +60,9 @@ export class NgxMatYearView implements AfterContentInit, OnDestroy { return this._activeDate; } set activeDate(value: D) { + if (!this._dateAdapter) + return; + const oldActiveDate = this._activeDate; const validDate = this._dateAdapter.getValidDateOrNull(this._dateAdapter.deserialize(value)) || @@ -78,7 +80,7 @@ export class NgxMatYearView implements AfterContentInit, OnDestroy { return this._selected; } set selected(value: NgxDateRange | D | null) { - if (value instanceof NgxDateRange) { + if (!this._dateAdapter || value instanceof NgxDateRange) { this._selected = value; } else { this._selected = this._dateAdapter.getValidDateOrNull(this._dateAdapter.deserialize(value)); @@ -94,6 +96,11 @@ export class NgxMatYearView implements AfterContentInit, OnDestroy { return this._minDate; } set minDate(value: D | null) { + if (!this._dateAdapter) { + this._minDate = null; + return; + } + this._minDate = this._dateAdapter.getValidDateOrNull(this._dateAdapter.deserialize(value)); } private _minDate: D | null = null; @@ -104,6 +111,11 @@ export class NgxMatYearView implements AfterContentInit, OnDestroy { return this._maxDate; } set maxDate(value: D | null) { + if (!this._dateAdapter) { + this._maxDate = null; + return; + } + this._maxDate = this._dateAdapter.getValidDateOrNull(this._dateAdapter.deserialize(value)); } private _maxDate: D | null = null; @@ -141,14 +153,12 @@ export class NgxMatYearView implements AfterContentInit, OnDestroy { */ _selectedMonth: number | null = null; - constructor( - readonly _changeDetectorRef: ChangeDetectorRef, - @Optional() - @Inject(MAT_DATE_FORMATS) - private _dateFormats: MatDateFormats, - @Optional() public _dateAdapter: DateAdapter, - @Optional() private _dir?: Directionality, - ) { + readonly _changeDetectorRef = inject(ChangeDetectorRef); + private _dateFormats = inject(MAT_DATE_FORMATS, { optional: true }); + public _dateAdapter = inject(DateAdapter, { optional: true }); + private _dir = inject(Directionality, { optional: true }); + + constructor() { if (!this._dateAdapter) { throw createMissingDateImplError('DateAdapter'); } @@ -160,6 +170,9 @@ export class NgxMatYearView implements AfterContentInit, OnDestroy { } ngAfterContentInit() { + if (!this._dateAdapter) + return; + this._rerenderSubscription = this._dateAdapter.localeChanges .pipe(startWith(null)) .subscribe(() => this._init()); @@ -171,6 +184,9 @@ export class NgxMatYearView implements AfterContentInit, OnDestroy { /** Handles when a new month is selected. */ _monthSelected(event: NgxMatCalendarUserEvent) { + if (!this._dateAdapter) + return; + const month = event.value; const selectedMonth = this._dateAdapter.createDate( @@ -181,7 +197,7 @@ export class NgxMatYearView implements AfterContentInit, OnDestroy { this.monthSelected.emit(selectedMonth); const selectedDate = this._getDateFromMonth(month); - this.selectedChange.emit(selectedDate); + this.selectedChange.emit(selectedDate!); } /** @@ -197,10 +213,10 @@ export class NgxMatYearView implements AfterContentInit, OnDestroy { _updateActiveDate(event: NgxMatCalendarUserEvent) { const month = event.value; const oldActiveDate = this._activeDate; + const dateToUse = this._getDateFromMonth(month); - this.activeDate = this._getDateFromMonth(month); - - if (this._dateAdapter.compareDate(oldActiveDate, this.activeDate)) { + if (this._dateAdapter && dateToUse && this._dateAdapter.compareDate(oldActiveDate, this.activeDate)) { + this.activeDate = dateToUse; this.activeDateChange.emit(this.activeDate); } } @@ -216,37 +232,37 @@ export class NgxMatYearView implements AfterContentInit, OnDestroy { switch (event.keyCode) { case LEFT_ARROW: - this.activeDate = this._dateAdapter.addCalendarMonths(this._activeDate, isRtl ? 1 : -1); + this.activeDate = this._dateAdapter?.addCalendarMonths(this._activeDate, isRtl ? 1 : -1); break; case RIGHT_ARROW: - this.activeDate = this._dateAdapter.addCalendarMonths(this._activeDate, isRtl ? -1 : 1); + this.activeDate = this._dateAdapter?.addCalendarMonths(this._activeDate, isRtl ? -1 : 1); break; case UP_ARROW: - this.activeDate = this._dateAdapter.addCalendarMonths(this._activeDate, -4); + this.activeDate = this._dateAdapter?.addCalendarMonths(this._activeDate, -4); break; case DOWN_ARROW: - this.activeDate = this._dateAdapter.addCalendarMonths(this._activeDate, 4); + this.activeDate = this._dateAdapter?.addCalendarMonths(this._activeDate, 4); break; case HOME: - this.activeDate = this._dateAdapter.addCalendarMonths( + this.activeDate = this._dateAdapter?.addCalendarMonths( this._activeDate, -this._dateAdapter.getMonth(this._activeDate), ); break; case END: - this.activeDate = this._dateAdapter.addCalendarMonths( + this.activeDate = this._dateAdapter?.addCalendarMonths( this._activeDate, 11 - this._dateAdapter.getMonth(this._activeDate), ); break; case PAGE_UP: - this.activeDate = this._dateAdapter.addCalendarYears( + this.activeDate = this._dateAdapter?.addCalendarYears( this._activeDate, event.altKey ? -10 : -1, ); break; case PAGE_DOWN: - this.activeDate = this._dateAdapter.addCalendarYears( + this.activeDate = this._dateAdapter?.addCalendarYears( this._activeDate, event.altKey ? 10 : 1, ); @@ -264,7 +280,7 @@ export class NgxMatYearView implements AfterContentInit, OnDestroy { return; } - if (this._dateAdapter.compareDate(oldActiveDate, this.activeDate)) { + if (this._dateAdapter && this._dateAdapter.compareDate(oldActiveDate, this.activeDate)) { this.activeDateChange.emit(this.activeDate); this._focusActiveCellAfterViewChecked(); } @@ -276,7 +292,7 @@ export class NgxMatYearView implements AfterContentInit, OnDestroy { /** Handles keyup events on the calendar body when calendar is in year view. */ _handleCalendarBodyKeyup(event: KeyboardEvent): void { if (event.keyCode === SPACE || event.keyCode === ENTER) { - if (this._selectionKeyPressed) { + if (this._dateAdapter && this._selectionKeyPressed) { this._monthSelected({ value: this._dateAdapter.getMonth(this._activeDate), event, @@ -289,6 +305,9 @@ export class NgxMatYearView implements AfterContentInit, OnDestroy { /** Initializes this year view. */ _init() { + if (!this._dateAdapter) + return; + this._setSelectedMonth(this.selected); this._todayMonth = this._getMonthInCurrentYear(this._dateAdapter.today()); this._yearLabel = this._dateAdapter.getYearName(this.activeDate); @@ -299,7 +318,11 @@ export class NgxMatYearView implements AfterContentInit, OnDestroy { [0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11], - ].map((row) => row.map((month) => this._createCellForMonth(month, monthNames[month]))); + ].map( + row => row + .map(m => this._createCellForMonth(m, monthNames[m])) + .filter(m => m != null) + ); this._changeDetectorRef.markForCheck(); } @@ -317,8 +340,8 @@ export class NgxMatYearView implements AfterContentInit, OnDestroy { * Gets the month in this year that the given Date falls on. * Returns null if the given Date is in another year. */ - private _getMonthInCurrentYear(date: D | null) { - return date && this._dateAdapter.getYear(date) == this._dateAdapter.getYear(this.activeDate) + private _getMonthInCurrentYear(date: D | null): number | null { + return this._dateAdapter && date && this._dateAdapter.getYear(date) == this._dateAdapter.getYear(this.activeDate) ? this._dateAdapter.getMonth(date) : null; } @@ -327,7 +350,10 @@ export class NgxMatYearView implements AfterContentInit, OnDestroy { * Takes a month and returns a new date in the same day and year as the currently active date. * The returned date will have the same month as the argument date. */ - private _getDateFromMonth(month: number) { + private _getDateFromMonth(month: number): D | null { + if (!this._dateAdapter) + return null; + const normalizedDate = this._dateAdapter.createDate( this._dateAdapter.getYear(this.activeDate), month, @@ -344,7 +370,10 @@ export class NgxMatYearView implements AfterContentInit, OnDestroy { } /** Creates an MatCalendarCell for the given month. */ - private _createCellForMonth(month: number, monthName: string) { + private _createCellForMonth(month: number, monthName: string): NgxMatCalendarCell | null { + if (!this._dateAdapter || !this._dateFormats) + return null; + const date = this._dateAdapter.createDate(this._dateAdapter.getYear(this.activeDate), month, 1); const ariaLabel = this._dateAdapter.format(date, this._dateFormats.display.monthYearA11yLabel); const cellClasses = this.dateClass() ? this.dateClass()!(date, 'year') : undefined; @@ -359,7 +388,10 @@ export class NgxMatYearView implements AfterContentInit, OnDestroy { } /** Whether the given month is enabled. */ - private _shouldEnableMonth(month: number) { + private _shouldEnableMonth(month: number): boolean { + if (!this._dateAdapter) + return false; + const activeYear = this._dateAdapter.getYear(this.activeDate); if ( @@ -395,8 +427,8 @@ export class NgxMatYearView implements AfterContentInit, OnDestroy { * Tests whether the combination month/year is after this.maxDate, considering * just the month and year of this.maxDate */ - private _isYearAndMonthAfterMaxDate(year: number, month: number) { - if (this.maxDate) { + private _isYearAndMonthAfterMaxDate(year: number, month: number): boolean { + if (this._dateAdapter && this.maxDate) { const maxYear = this._dateAdapter.getYear(this.maxDate); const maxMonth = this._dateAdapter.getMonth(this.maxDate); @@ -410,8 +442,8 @@ export class NgxMatYearView implements AfterContentInit, OnDestroy { * Tests whether the combination month/year is before this.minDate, considering * just the month and year of this.minDate */ - private _isYearAndMonthBeforeMinDate(year: number, month: number) { - if (this.minDate) { + private _isYearAndMonthBeforeMinDate(year: number, month: number): boolean { + if (this._dateAdapter && this.minDate) { const minYear = this._dateAdapter.getYear(this.minDate); const minMonth = this._dateAdapter.getMonth(this.minDate); From de53e29b8a5426e98585491309950e7facd5abbd Mon Sep 17 00:00:00 2001 From: Felipe BF Date: Thu, 2 Apr 2026 16:05:41 -0700 Subject: [PATCH 06/11] refactor: use inject calls for multi-year view --- .../src/lib/multi-year-view.ts | 92 ++++++++++++++----- 1 file changed, 67 insertions(+), 25 deletions(-) diff --git a/projects/datetime-picker/src/lib/multi-year-view.ts b/projects/datetime-picker/src/lib/multi-year-view.ts index 25830faa..98a79e8a 100644 --- a/projects/datetime-picker/src/lib/multi-year-view.ts +++ b/projects/datetime-picker/src/lib/multi-year-view.ts @@ -18,8 +18,8 @@ import { Component, Input, OnDestroy, - Optional, ViewEncapsulation, + inject, input, output, viewChild, @@ -64,6 +64,9 @@ export class NgxMatMultiYearView implements AfterContentInit, OnDestroy { return this._activeDate; } set activeDate(value: D) { + if (!this._dateAdapter) + return; + const oldActiveDate = this._activeDate; const validDate = this._dateAdapter.getValidDateOrNull(this._dateAdapter.deserialize(value)) || @@ -90,7 +93,7 @@ export class NgxMatMultiYearView implements AfterContentInit, OnDestroy { return this._selected; } set selected(value: NgxDateRange | D | null) { - if (value instanceof NgxDateRange) { + if (!this._dateAdapter || value instanceof NgxDateRange) { this._selected = value; } else { this._selected = this._dateAdapter.getValidDateOrNull(this._dateAdapter.deserialize(value)); @@ -106,6 +109,11 @@ export class NgxMatMultiYearView implements AfterContentInit, OnDestroy { return this._minDate; } set minDate(value: D | null) { + if (!this._dateAdapter) { + this._minDate = null; + return; + } + this._minDate = this._dateAdapter.getValidDateOrNull(this._dateAdapter.deserialize(value)); } private _minDate: D | null = null; @@ -116,6 +124,11 @@ export class NgxMatMultiYearView implements AfterContentInit, OnDestroy { return this._maxDate; } set maxDate(value: D | null) { + if (!this._dateAdapter) { + this._maxDate = null; + return; + } + this._maxDate = this._dateAdapter.getValidDateOrNull(this._dateAdapter.deserialize(value)); } private _maxDate: D | null = null; @@ -147,11 +160,11 @@ export class NgxMatMultiYearView implements AfterContentInit, OnDestroy { /** The year of the selected date. Null if the selected date is null. */ _selectedYear: number | null = null; - constructor( - private _changeDetectorRef: ChangeDetectorRef, - @Optional() public _dateAdapter: DateAdapter, - @Optional() private _dir?: Directionality, - ) { + private _changeDetectorRef = inject(ChangeDetectorRef); + public _dateAdapter = inject(DateAdapter, { optional: true }); + private _dir = inject(Directionality, { optional: true }); + + constructor() { if (!this._dateAdapter) { throw createMissingDateImplError('DateAdapter'); } @@ -160,6 +173,9 @@ export class NgxMatMultiYearView implements AfterContentInit, OnDestroy { } ngAfterContentInit() { + if (!this._dateAdapter) + return; + this._rerenderSubscription = this._dateAdapter.localeChanges .pipe(startWith(null)) .subscribe(() => this._init()); @@ -171,6 +187,9 @@ export class NgxMatMultiYearView implements AfterContentInit, OnDestroy { /** Initializes this multi-year view. */ _init() { + if (!this._dateAdapter) + return; + this._todayYear = this._dateAdapter.getYear(this._dateAdapter.today()); // We want a range years such that we maximize the number of @@ -187,7 +206,11 @@ export class NgxMatMultiYearView implements AfterContentInit, OnDestroy { for (let i = 0, row: number[] = []; i < yearsPerPage; i++) { row.push(minYearOfPage + i); if (row.length == yearsPerRow) { - this._years.push(row.map((year) => this._createCellForYear(year))); + this._years.push( + row + .map(y => this._createCellForYear(y)) + .filter(y => y != null) + ); row = []; } } @@ -196,12 +219,15 @@ export class NgxMatMultiYearView implements AfterContentInit, OnDestroy { /** Handles when a new year is selected. */ _yearSelected(event: NgxMatCalendarUserEvent) { + if (!this._dateAdapter) + return; + const year = event.value; const selectedYear = this._dateAdapter.createDate(year, 0, 1); const selectedDate = this._getDateFromYear(year); this.yearSelected.emit(selectedYear); - this.selectedChange.emit(selectedDate); + this.selectedChange.emit(selectedDate!); } /** @@ -217,9 +243,10 @@ export class NgxMatMultiYearView implements AfterContentInit, OnDestroy { _updateActiveDate(event: NgxMatCalendarUserEvent) { const year = event.value; const oldActiveDate = this._activeDate; + const dateToUse = this._getDateFromYear(year); - this.activeDate = this._getDateFromYear(year); - if (this._dateAdapter.compareDate(oldActiveDate, this.activeDate)) { + if (this._dateAdapter && dateToUse && this._dateAdapter.compareDate(oldActiveDate, this.activeDate)) { + this.activeDate = dateToUse this.activeDateChange.emit(this.activeDate); } } @@ -231,25 +258,25 @@ export class NgxMatMultiYearView implements AfterContentInit, OnDestroy { switch (event.keyCode) { case LEFT_ARROW: - this.activeDate = this._dateAdapter.addCalendarYears(this._activeDate, isRtl ? 1 : -1); + this.activeDate = this._dateAdapter?.addCalendarYears(this._activeDate, isRtl ? 1 : -1); break; case RIGHT_ARROW: - this.activeDate = this._dateAdapter.addCalendarYears(this._activeDate, isRtl ? -1 : 1); + this.activeDate = this._dateAdapter?.addCalendarYears(this._activeDate, isRtl ? -1 : 1); break; case UP_ARROW: - this.activeDate = this._dateAdapter.addCalendarYears(this._activeDate, -yearsPerRow); + this.activeDate = this._dateAdapter?.addCalendarYears(this._activeDate, -yearsPerRow); break; case DOWN_ARROW: - this.activeDate = this._dateAdapter.addCalendarYears(this._activeDate, yearsPerRow); + this.activeDate = this._dateAdapter?.addCalendarYears(this._activeDate, yearsPerRow); break; case HOME: - this.activeDate = this._dateAdapter.addCalendarYears( + this.activeDate = this._dateAdapter?.addCalendarYears( this._activeDate, -getActiveOffset(this._dateAdapter, this.activeDate, this.minDate, this.maxDate), ); break; case END: - this.activeDate = this._dateAdapter.addCalendarYears( + this.activeDate = this._dateAdapter?.addCalendarYears( this._activeDate, yearsPerPage - getActiveOffset(this._dateAdapter, this.activeDate, this.minDate, this.maxDate) - @@ -257,13 +284,13 @@ export class NgxMatMultiYearView implements AfterContentInit, OnDestroy { ); break; case PAGE_UP: - this.activeDate = this._dateAdapter.addCalendarYears( + this.activeDate = this._dateAdapter?.addCalendarYears( this._activeDate, event.altKey ? -yearsPerPage * 10 : -yearsPerPage, ); break; case PAGE_DOWN: - this.activeDate = this._dateAdapter.addCalendarYears( + this.activeDate = this._dateAdapter?.addCalendarYears( this._activeDate, event.altKey ? yearsPerPage * 10 : yearsPerPage, ); @@ -280,7 +307,7 @@ export class NgxMatMultiYearView implements AfterContentInit, OnDestroy { // Don't prevent default or focus active cell on keys that we don't explicitly handle. return; } - if (this._dateAdapter.compareDate(oldActiveDate, this.activeDate)) { + if (this._dateAdapter && this._dateAdapter.compareDate(oldActiveDate, this.activeDate)) { this.activeDateChange.emit(this.activeDate); } @@ -292,7 +319,7 @@ export class NgxMatMultiYearView implements AfterContentInit, OnDestroy { /** Handles keyup events on the calendar body when calendar is in multi-year view. */ _handleCalendarBodyKeyup(event: KeyboardEvent): void { if (event.keyCode === SPACE || event.keyCode === ENTER) { - if (this._selectionKeyPressed) { + if (this._dateAdapter && this._selectionKeyPressed) { this._yearSelected({ value: this._dateAdapter.getYear(this._activeDate), event, @@ -321,7 +348,10 @@ export class NgxMatMultiYearView implements AfterContentInit, OnDestroy { * Takes a year and returns a new date on the same day and month as the currently active date * The returned date will have the same year as the argument date. */ - private _getDateFromYear(year: number) { + private _getDateFromYear(year: number): D | null { + if (!this._dateAdapter) + return null; + const activeMonth = this._dateAdapter.getMonth(this.activeDate); const daysInMonth = this._dateAdapter.getNumDaysInMonth( this._dateAdapter.createDate(year, activeMonth, 1), @@ -335,7 +365,10 @@ export class NgxMatMultiYearView implements AfterContentInit, OnDestroy { } /** Creates an MatCalendarCell for the given year. */ - private _createCellForYear(year: number) { + private _createCellForYear(year: number): NgxMatCalendarCell | null { + if (!this._dateAdapter) + return null; + const date = this._dateAdapter.createDate(year, 0, 1); const yearName = this._dateAdapter.getYearName(date); const cellClasses = this.dateClass() ? this.dateClass()!(date, 'multi-year') : undefined; @@ -350,7 +383,10 @@ export class NgxMatMultiYearView implements AfterContentInit, OnDestroy { } /** Whether the given year is enabled. */ - private _shouldEnableYear(year: number) { + private _shouldEnableYear(year: number): boolean { + if (!this._dateAdapter) + return false; + // disable if the year is greater than maxDate lower than minDate if ( year === undefined || @@ -389,6 +425,9 @@ export class NgxMatMultiYearView implements AfterContentInit, OnDestroy { /** Sets the currently-highlighted year based on a model value. */ private _setSelectedYear(value: NgxDateRange | D | null) { + if (!this._dateAdapter) + return; + this._selectedYear = null; if (value instanceof NgxDateRange) { @@ -425,11 +464,14 @@ export function isSameMultiYearView( * "startingYear" will render when paged into view. */ export function getActiveOffset( - dateAdapter: DateAdapter, + dateAdapter: DateAdapter | null, activeDate: D, minDate: D | null, maxDate: D | null, ): number { + if (!dateAdapter) + return 0; + const activeYear = dateAdapter.getYear(activeDate); return euclideanModulo(activeYear - getStartingYear(dateAdapter, minDate, maxDate), yearsPerPage); } From 245429ae316a4efa1b404082d36c1bb61ce457e0 Mon Sep 17 00:00:00 2001 From: Felipe BF Date: Thu, 2 Apr 2026 16:41:28 -0700 Subject: [PATCH 07/11] refactor: use inject calls in other datepicker components --- .../src/lib/date-range-selection-strategy.ts | 9 +++---- .../src/lib/date-selection-model.ts | 25 +++++++++---------- .../src/lib/datepicker-actions.ts | 21 ++++++---------- .../src/lib/datepicker-toggle.ts | 6 +++-- 4 files changed, 27 insertions(+), 34 deletions(-) diff --git a/projects/datetime-picker/src/lib/date-range-selection-strategy.ts b/projects/datetime-picker/src/lib/date-range-selection-strategy.ts index 9acb2356..a8f84e22 100644 --- a/projects/datetime-picker/src/lib/date-range-selection-strategy.ts +++ b/projects/datetime-picker/src/lib/date-range-selection-strategy.ts @@ -1,4 +1,4 @@ -import { FactoryProvider, Injectable, InjectionToken, Optional, SkipSelf } from '@angular/core'; +import { FactoryProvider, inject, Injectable, InjectionToken, Optional, SkipSelf } from '@angular/core'; import { DateAdapter } from '@angular/material/core'; import { NgxDateRange } from './date-selection-model'; @@ -51,8 +51,8 @@ export interface NgxMatDateRangeSelectionStrategy { /** Provides the default date range selection behavior. */ @Injectable() export class DefaultNgxMatCalendarRangeStrategy implements NgxMatDateRangeSelectionStrategy { - constructor(private _dateAdapter: DateAdapter) {} - + private _dateAdapter = inject(DateAdapter); + selectionFinished(date: D, currentRange: NgxDateRange) { let { start, end } = currentRange; @@ -126,9 +126,8 @@ export class DefaultNgxMatCalendarRangeStrategy implements NgxMatDateRangeSel /** @docs-private */ export function NGX_MAT_CALENDAR_RANGE_STRATEGY_PROVIDER_FACTORY( parent: NgxMatDateRangeSelectionStrategy, - adapter: DateAdapter, ) { - return parent || new DefaultNgxMatCalendarRangeStrategy(adapter); + return parent || new DefaultNgxMatCalendarRangeStrategy(); } export const NGX_MAT_CALENDAR_RANGE_STRATEGY_PROVIDER: FactoryProvider = { diff --git a/projects/datetime-picker/src/lib/date-selection-model.ts b/projects/datetime-picker/src/lib/date-selection-model.ts index ad477161..1815d5e8 100644 --- a/projects/datetime-picker/src/lib/date-selection-model.ts +++ b/projects/datetime-picker/src/lib/date-selection-model.ts @@ -1,4 +1,4 @@ -import { FactoryProvider, Injectable, OnDestroy, Optional, SkipSelf } from '@angular/core'; +import { FactoryProvider, inject, Injectable, OnDestroy, Optional, SkipSelf } from '@angular/core'; import { DateAdapter } from '@angular/material/core'; import { Observable, Subject } from 'rxjs'; @@ -53,10 +53,11 @@ export abstract class NgxMatDateSelectionModel> = this._selectionChanged; + protected _adapter = inject(DateAdapter); + protected constructor( /** The current selection. */ - readonly selection: S, - protected _adapter: DateAdapter, + readonly selection: S ) { this.selection = selection; } @@ -99,8 +100,8 @@ export abstract class NgxMatDateSelectionModel extends NgxMatDateSelectionModel { - constructor(adapter: DateAdapter) { - super(null, adapter); + constructor() { + super(null); } /** @@ -126,7 +127,7 @@ export class NgxMatSingleDateSelectionModel extends NgxMatDateSelectionModel< /** Clones the selection model. */ clone() { - const clone = new NgxMatSingleDateSelectionModel(this._adapter); + const clone = new NgxMatSingleDateSelectionModel(); clone.updateSelection(this.selection, this); return clone; } @@ -138,8 +139,8 @@ export class NgxMatSingleDateSelectionModel extends NgxMatDateSelectionModel< */ @Injectable() export class NgxMatRangeDateSelectionModel extends NgxMatDateSelectionModel, D> { - constructor(adapter: DateAdapter) { - super(new NgxDateRange(null, null), adapter); + constructor() { + super(new NgxDateRange(null, null)); } /** @@ -197,7 +198,7 @@ export class NgxMatRangeDateSelectionModel extends NgxMatDateSelectionModel(this._adapter); + const clone = new NgxMatRangeDateSelectionModel(); clone.updateSelection(this.selection, this); return clone; } @@ -206,9 +207,8 @@ export class NgxMatRangeDateSelectionModel extends NgxMatDateSelectionModel, - adapter: DateAdapter, ) { - return parent || new NgxMatSingleDateSelectionModel(adapter); + return parent || new NgxMatSingleDateSelectionModel(); } /** @@ -224,9 +224,8 @@ export const NGX_MAT_SINGLE_DATE_SELECTION_MODEL_PROVIDER: FactoryProvider = { /** @docs-private */ export function NGX_MAT_RANGE_DATE_SELECTION_MODEL_FACTORY( parent: NgxMatSingleDateSelectionModel, - adapter: DateAdapter, ) { - return parent || new NgxMatRangeDateSelectionModel(adapter); + return parent || new NgxMatRangeDateSelectionModel(); } /** diff --git a/projects/datetime-picker/src/lib/datepicker-actions.ts b/projects/datetime-picker/src/lib/datepicker-actions.ts index b81dbcfd..909d4f58 100644 --- a/projects/datetime-picker/src/lib/datepicker-actions.ts +++ b/projects/datetime-picker/src/lib/datepicker-actions.ts @@ -10,6 +10,7 @@ import { ViewContainerRef, ViewEncapsulation, booleanAttribute, + inject, viewChild, } from '@angular/core'; import { NgxMatDatepickerBase, NgxMatDatepickerControl } from './datepicker-base'; @@ -20,9 +21,7 @@ import { NgxMatDatepickerBase, NgxMatDatepickerControl } from './datepicker-base host: { '(click)': '_applySelection()' }, }) export class NgxMatDatepickerApply { - constructor( - public readonly _datepicker: NgxMatDatepickerBase, unknown>, - ) {} + public readonly _datepicker = inject(NgxMatDatepickerBase, unknown>); _applySelection() { this._datepicker._applyPendingSelection(); @@ -35,10 +34,8 @@ export class NgxMatDatepickerApply { host: { '(click)': '_clearSelection()' }, }) export class NgxMatDatepickerClear { - constructor( - public readonly _datepicker: NgxMatDatepickerBase, unknown>, - ) {} - + public readonly _datepicker = inject(NgxMatDatepickerBase, unknown>); + @Input({ transform: booleanAttribute, }) @@ -58,9 +55,7 @@ export class NgxMatDatepickerClear { host: { '(click)': '_datepicker.close()' }, }) export class NgxMatDatepickerCancel { - constructor( - public readonly _datepicker: NgxMatDatepickerBase, unknown>, - ) {} + public readonly _datepicker = inject(NgxMatDatepickerBase, unknown>); } /** @@ -84,10 +79,8 @@ export class NgxMatDatepickerActions implements AfterViewInit, OnDestroy { _template = viewChild>(TemplateRef); private _portal: TemplatePortal | null = null; - constructor( - private _datepicker: NgxMatDatepickerBase, unknown>, - private _viewContainerRef: ViewContainerRef, - ) {} + private _datepicker = inject(NgxMatDatepickerBase, unknown>); + private _viewContainerRef = inject(ViewContainerRef); ngAfterViewInit() { this._portal = new TemplatePortal(this._template()!, this._viewContainerRef); diff --git a/projects/datetime-picker/src/lib/datepicker-toggle.ts b/projects/datetime-picker/src/lib/datepicker-toggle.ts index 6131cb6e..4a207f49 100644 --- a/projects/datetime-picker/src/lib/datepicker-toggle.ts +++ b/projects/datetime-picker/src/lib/datepicker-toggle.ts @@ -10,6 +10,7 @@ import { OnDestroy, ViewEncapsulation, effect, + inject, input, untracked, viewChild, @@ -82,9 +83,10 @@ export class NgxMatDatepickerToggle implements AfterContentInit, OnDestroy { /** Underlying button element. */ _button = viewChild('button'); + public _intl = inject(NgxMatDatepickerIntl); + private _changeDetectorRef = inject(ChangeDetectorRef); + constructor( - public _intl: NgxMatDatepickerIntl, - private _changeDetectorRef: ChangeDetectorRef, @Attribute('tabindex') defaultTabIndex: string, ) { const parsedTabIndex = Number(defaultTabIndex); From 9a169b138ca15b11d9440b94ea17f9eea405422c Mon Sep 17 00:00:00 2001 From: Felipe BF Date: Thu, 2 Apr 2026 16:41:43 -0700 Subject: [PATCH 08/11] refactor: use inject calls in calendar base component --- .../datetime-picker/src/lib/calendar-body.ts | 12 +-- projects/datetime-picker/src/lib/calendar.ts | 91 +++++++++++++------ 2 files changed, 68 insertions(+), 35 deletions(-) diff --git a/projects/datetime-picker/src/lib/calendar-body.ts b/projects/datetime-picker/src/lib/calendar-body.ts index e8f8f753..48029054 100644 --- a/projects/datetime-picker/src/lib/calendar-body.ts +++ b/projects/datetime-picker/src/lib/calendar-body.ts @@ -169,12 +169,12 @@ export class NgxMatCalendarBody implements OnDestroy, AfterViewChecked private _didDragSinceMouseDown = false; - constructor( - private _elementRef: ElementRef, - private _ngZone: NgZone, - ) { - _ngZone.runOutsideAngular(() => { - const element = _elementRef.nativeElement; + private _elementRef = inject(ElementRef); + private _ngZone = inject(NgZone); + + constructor() { + this._ngZone.runOutsideAngular(() => { + const element = this._elementRef.nativeElement; element.addEventListener('mouseenter', this._enterHandler, true); element.addEventListener('touchmove', this._touchmoveHandler, true); element.addEventListener('focus', this._enterHandler, true); diff --git a/projects/datetime-picker/src/lib/calendar.ts b/projects/datetime-picker/src/lib/calendar.ts index a528ccf9..66c42c6a 100644 --- a/projects/datetime-picker/src/lib/calendar.ts +++ b/projects/datetime-picker/src/lib/calendar.ts @@ -6,15 +6,14 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, - Inject, Input, OnChanges, OnDestroy, - Optional, SimpleChange, SimpleChanges, ViewEncapsulation, forwardRef, + inject, input, linkedSignal, output, @@ -22,7 +21,7 @@ import { } from '@angular/core'; import { outputFromObservable, toObservable } from '@angular/core/rxjs-interop'; import { MatButton, MatIconButton } from '@angular/material/button'; -import { DateAdapter, MAT_DATE_FORMATS, MatDateFormats } from '@angular/material/core'; +import { DateAdapter, MAT_DATE_FORMATS } from '@angular/material/core'; import { Subject, Subscription } from 'rxjs'; import { NgxMatCalendarCellClassFunction, NgxMatCalendarUserEvent } from './calendar-body'; import { NGX_MAT_SINGLE_DATE_SELECTION_MODEL_PROVIDER, NgxDateRange } from './date-selection-model'; @@ -55,21 +54,21 @@ export type NgxMatCalendarView = 'month' | 'year' | 'multi-year'; imports: [MatButton, MatIconButton], }) export class NgxMatCalendarHeader { - constructor( - private _intl: NgxMatDatepickerIntl, - @Inject(forwardRef(() => NgxMatCalendar)) - public calendar: NgxMatCalendar, - @Optional() private _dateAdapter: DateAdapter, - @Optional() - @Inject(MAT_DATE_FORMATS) - private _dateFormats: MatDateFormats, - changeDetectorRef: ChangeDetectorRef, - ) { - this.calendar.stateChanges.subscribe(() => changeDetectorRef.markForCheck()); + private _intl = inject(NgxMatDatepickerIntl); + public calendar: NgxMatCalendar = inject(forwardRef(() => NgxMatCalendar)); + private _dateAdapter = inject(DateAdapter, { optional: true }); + private _dateFormats = inject(MAT_DATE_FORMATS, { optional: true }); + changeDetectorRef = inject(ChangeDetectorRef); + + constructor() { + this.calendar.stateChanges.subscribe(() => this.changeDetectorRef.markForCheck()); } /** The display text for the current calendar view. */ get periodButtonText(): string { + if (!this._dateAdapter || !this._dateFormats) + return ""; + if (this.calendar.currentView() == 'month') { return this._dateAdapter .format(this.calendar.activeDate!, this._dateFormats.display.monthYearLabel) @@ -84,6 +83,9 @@ export class NgxMatCalendarHeader { /** The aria description for the current calendar view. */ get periodButtonDescription(): string { + if (!this._dateAdapter || !this._dateFormats) + return ""; + if (this.calendar.currentView() == 'month') { return this._dateAdapter .format(this.calendar.activeDate!, this._dateFormats.display.monthYearLabel) @@ -130,6 +132,9 @@ export class NgxMatCalendarHeader { /** Handles user clicks on the previous button. */ previousClicked(): void { + if (!this._dateAdapter) + return; + this.calendar.activeDate = this.calendar.currentView() == 'month' ? this._dateAdapter.addCalendarMonths(this.calendar.activeDate!, -1) @@ -141,6 +146,9 @@ export class NgxMatCalendarHeader { /** Handles user clicks on the next button. */ nextClicked(): void { + if (!this._dateAdapter) + return; + this.calendar.activeDate = this.calendar.currentView() == 'month' ? this._dateAdapter.addCalendarMonths(this.calendar.activeDate!, 1) @@ -169,6 +177,9 @@ export class NgxMatCalendarHeader { /** Whether the two dates represent the same view in the current view mode (month or year). */ private _isSameView(date1: D, date2: D): boolean { + if (!this._dateAdapter) + return false; + if (this.calendar.currentView() == 'month') { return ( this._dateAdapter.getYear(date1) == this._dateAdapter.getYear(date2) && @@ -194,6 +205,9 @@ export class NgxMatCalendarHeader { * for the minimum year, and the second string is the formatted label for the maximum year. */ private _formatMinAndMaxYearLabels(): [minYearLabel: string, maxYearLabel: string] { + if (!this._dateAdapter) + return ["", ""]; + // The offset from the active year to the "slot" for the starting year is the // *actual* first rendered year in the multi-year view, and the last year is // just yearsPerPage - 1 away. @@ -258,7 +272,9 @@ export class NgxMatCalendar implements AfterContentInit, AfterViewChecked, On return this._startAt; } set startAt(value: D | null) { - this._startAt = this._dateAdapter.getValidDateOrNull(this._dateAdapter.deserialize(value)); + this._startAt = this._dateAdapter + ? this._dateAdapter.getValidDateOrNull(this._dateAdapter.deserialize(value)) + : null; } private _startAt: D | null = null; @@ -271,7 +287,7 @@ export class NgxMatCalendar implements AfterContentInit, AfterViewChecked, On return this._selected; } set selected(value: NgxDateRange | D | null) { - if (value instanceof NgxDateRange) { + if (!this._dateAdapter || value instanceof NgxDateRange) { this._selected = value; } else { this._selected = this._dateAdapter.getValidDateOrNull(this._dateAdapter.deserialize(value)); @@ -285,6 +301,11 @@ export class NgxMatCalendar implements AfterContentInit, AfterViewChecked, On return this._minDate; } set minDate(value: D | null) { + if (!this._dateAdapter) { + this._minDate = null; + return; + } + this._minDate = this._dateAdapter.getValidDateOrNull(this._dateAdapter.deserialize(value)); } private _minDate: D | null = null; @@ -295,6 +316,11 @@ export class NgxMatCalendar implements AfterContentInit, AfterViewChecked, On return this._maxDate; } set maxDate(value: D | null) { + if (!this._dateAdapter) { + this._maxDate = null; + return; + } + this._maxDate = this._dateAdapter.getValidDateOrNull(this._dateAdapter.deserialize(value)); } private _maxDate: D | null = null; @@ -359,9 +385,12 @@ export class NgxMatCalendar implements AfterContentInit, AfterViewChecked, On * highlighted when using keyboard navigation. */ get activeDate(): D { - return this._clampedActiveDate ?? this._dateAdapter.today(); + return this._clampedActiveDate ?? this._dateAdapter?.today(); } set activeDate(value: D) { + if (!this._dateAdapter) + return; + this._clampedActiveDate = this._dateAdapter.clampDate(value, this.minDate, this.maxDate); this.stateChanges.next(); this._changeDetectorRef.markForCheck(); @@ -376,14 +405,12 @@ export class NgxMatCalendar implements AfterContentInit, AfterViewChecked, On */ readonly stateChanges = new Subject(); - constructor( - _intl: NgxMatDatepickerIntl, - @Optional() private _dateAdapter: DateAdapter, - @Optional() - @Inject(MAT_DATE_FORMATS) - private _dateFormats: MatDateFormats, - private _changeDetectorRef: ChangeDetectorRef, - ) { + _intl = inject(NgxMatDatepickerIntl); + private _dateAdapter = inject(DateAdapter, { optional: true }); + private _dateFormats = inject(MAT_DATE_FORMATS, { optional: true }); + private _changeDetectorRef = inject(ChangeDetectorRef); + + constructor() { if (!this._dateAdapter) { throw createMissingDateImplError('DateAdapter'); } @@ -392,13 +419,16 @@ export class NgxMatCalendar implements AfterContentInit, AfterViewChecked, On throw createMissingDateImplError('MAT_DATE_FORMATS'); } - this._intlChanges = _intl.changes.subscribe(() => { - _changeDetectorRef.markForCheck(); + this._intlChanges = this._intl.changes.subscribe(() => { + this._changeDetectorRef.markForCheck(); this.stateChanges.next(); }); } ngAfterContentInit() { + if (!this._dateAdapter) + return; + this._calendarHeaderPortal = new ComponentPortal( this.headerComponent() || NgxMatCalendarHeader, ); @@ -423,12 +453,12 @@ export class NgxMatCalendar implements AfterContentInit, AfterViewChecked, On // the calendar re-renders when there is no meaningful change to [minDate] or [maxDate] // (#24435). const minDateChange: SimpleChange | undefined = - changes['minDate'] && + this._dateAdapter && changes['minDate'] && !this._dateAdapter.sameDate(changes['minDate'].previousValue, changes['minDate'].currentValue) ? changes['minDate'] : undefined; const maxDateChange: SimpleChange | undefined = - changes['maxDate'] && + this._dateAdapter && changes['maxDate'] && !this._dateAdapter.sameDate(changes['maxDate'].previousValue, changes['maxDate'].currentValue) ? changes['maxDate'] : undefined; @@ -462,6 +492,9 @@ export class NgxMatCalendar implements AfterContentInit, AfterViewChecked, On /** Handles date selection in the month view. */ _dateSelected(event: NgxMatCalendarUserEvent): void { + if (!this._dateAdapter) + return; + let date = event.value as D; if (date && this.selected) { const selected = this.selected as D; From 8e1ba61ccee1b33a3792f0f59fe1bbc2d09f31c3 Mon Sep 17 00:00:00 2001 From: Felipe BF Date: Thu, 2 Apr 2026 16:48:39 -0700 Subject: [PATCH 09/11] refactor: comment empty functions for intention --- .../datetime-picker/src/lib/datepicker-input-base.ts | 12 +++++++++--- .../src/lib/datetime-picker-input-v2.directive.ts | 12 +++++++++--- .../datetime-picker/src/lib/timepicker.component.ts | 8 ++++++-- 3 files changed, 24 insertions(+), 8 deletions(-) diff --git a/projects/datetime-picker/src/lib/datepicker-input-base.ts b/projects/datetime-picker/src/lib/datepicker-input-base.ts index 5bcf24fc..ca53f847 100644 --- a/projects/datetime-picker/src/lib/datepicker-input-base.ts +++ b/projects/datetime-picker/src/lib/datepicker-input-base.ts @@ -122,10 +122,16 @@ export abstract class NgxMatDatepickerInputBase(); - _onTouched = () => {}; - _validatorOnChange = () => {}; + _onTouched = () => { + // Intentionally left empty. + }; + _validatorOnChange = () => { + // Intentionally left empty. + }; - private _cvaOnChange: (value: any) => void = () => {}; + private _cvaOnChange: (value: any) => void = () => { + // Intentionally left empty. + }; private _valueChangesSubscription = Subscription.EMPTY; private _localeSubscription = Subscription.EMPTY; diff --git a/projects/datetime-picker/src/lib/datetime-picker-input-v2.directive.ts b/projects/datetime-picker/src/lib/datetime-picker-input-v2.directive.ts index fe3464f9..0887bb2f 100644 --- a/projects/datetime-picker/src/lib/datetime-picker-input-v2.directive.ts +++ b/projects/datetime-picker/src/lib/datetime-picker-input-v2.directive.ts @@ -162,10 +162,16 @@ export class NgxMatDatetimePickerInputV2 /** Emits when the internal state has changed */ readonly stateChanges = new Subject(); - _onTouched = () => {}; - _validatorOnChange = () => {}; + _onTouched = () => { + // Intentionally left empty. + }; + _validatorOnChange = () => { + // Intentionally left empty. + }; - private _cvaOnChange: (value: any) => void = () => {}; + private _cvaOnChange: (value: any) => void = () => { + // Intentionally left empty. + }; private _valueChangesSubscription = Subscription.EMPTY; private _localeSubscription = Subscription.EMPTY; diff --git a/projects/datetime-picker/src/lib/timepicker.component.ts b/projects/datetime-picker/src/lib/timepicker.component.ts index 0056b748..248ae22b 100644 --- a/projects/datetime-picker/src/lib/timepicker.component.ts +++ b/projects/datetime-picker/src/lib/timepicker.component.ts @@ -104,8 +104,12 @@ export class NgxMatTimepickerComponent implements ControlValueAccessor { }), }); - private _onChange: any = () => {}; - private _onTouched: any = () => {}; + private _onChange: any = () => { + // Intentionally left empty. + }; + private _onTouched: any = () => { + // Intentionally left empty. + }; public readonly value = model(); /** Hour */ From b8d791e3be605121b0f4d961a3a3473a469ba827 Mon Sep 17 00:00:00 2001 From: Felipe BF Date: Thu, 2 Apr 2026 16:49:03 -0700 Subject: [PATCH 10/11] refactor: underscore unused parameters in functions --- .../src/lib/datetime-picker-content-v2.component.ts | 4 ++-- .../src/lib/datetime-picker-input-v2.directive.ts | 4 ++-- .../datetime-picker/src/lib/datetime-picker-v2.component.ts | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/projects/datetime-picker/src/lib/datetime-picker-content-v2.component.ts b/projects/datetime-picker/src/lib/datetime-picker-content-v2.component.ts index ef203aca..8054575b 100644 --- a/projects/datetime-picker/src/lib/datetime-picker-content-v2.component.ts +++ b/projects/datetime-picker/src/lib/datetime-picker-content-v2.component.ts @@ -171,11 +171,11 @@ export class NgxMatDatetimePickerContentV2 implements OnInit, OnDestroy { } } - onYearSelected(year: D): void { + onYearSelected(_: D): void { // Let the calendar handle year selection } - onMonthSelected(month: D): void { + onMonthSelected(_: D): void { // Let the calendar handle month selection } diff --git a/projects/datetime-picker/src/lib/datetime-picker-input-v2.directive.ts b/projects/datetime-picker/src/lib/datetime-picker-input-v2.directive.ts index 0887bb2f..6f1512c0 100644 --- a/projects/datetime-picker/src/lib/datetime-picker-input-v2.directive.ts +++ b/projects/datetime-picker/src/lib/datetime-picker-input-v2.directive.ts @@ -367,7 +367,7 @@ export class NgxMatDatetimePickerInputV2 this.stateChanges.next(); } - _onClick(event: MouseEvent): void { + _onClick(_: MouseEvent): void { // Open the datepicker when clicking on the input if (this.ngxMatDatetimePicker && !this.disabled) { this.ngxMatDatetimePicker.open(); @@ -426,7 +426,7 @@ export class NgxMatDatetimePickerInputV2 } // MatFormFieldControl methods - setDescribedByIds(ids: string[]): void { + setDescribedByIds(_: string[]): void { // Implementation for accessibility } diff --git a/projects/datetime-picker/src/lib/datetime-picker-v2.component.ts b/projects/datetime-picker/src/lib/datetime-picker-v2.component.ts index 047bdc77..44f24eb8 100644 --- a/projects/datetime-picker/src/lib/datetime-picker-v2.component.ts +++ b/projects/datetime-picker/src/lib/datetime-picker-v2.component.ts @@ -130,7 +130,7 @@ export class NgxMatDatetimePickerV2 implements OnDestroy { } /** Applies the current pending selection on the overlay to the model. */ - select(date: D): void { + select(_: D): void { // This will be handled by the content component } From 948579829f2841324141d44f57617f9478f33c21 Mon Sep 17 00:00:00 2001 From: Felipe BF Date: Thu, 2 Apr 2026 17:05:28 -0700 Subject: [PATCH 11/11] refactor: correct other linter problems in datetime-picker --- projects/datetime-picker/src/lib/datepicker-base.ts | 5 ++++- projects/datetime-picker/src/lib/datepicker-input-base.ts | 2 +- .../src/lib/datetime-picker-content-v2.component.ts | 1 - .../src/lib/datetime-picker-input-v2.directive.ts | 7 ++++--- projects/datetime-picker/src/lib/timepicker.component.ts | 4 ++-- 5 files changed, 11 insertions(+), 8 deletions(-) diff --git a/projects/datetime-picker/src/lib/datepicker-base.ts b/projects/datetime-picker/src/lib/datepicker-base.ts index 99402313..2e551510 100644 --- a/projects/datetime-picker/src/lib/datepicker-base.ts +++ b/projects/datetime-picker/src/lib/datepicker-base.ts @@ -565,7 +565,10 @@ export abstract class NgxMatDatepickerBase< } set opened(value: BooleanInput) { - coerceBooleanProperty(value) ? this.open() : this.close(); + if (coerceBooleanProperty(value)) + this.open(); + else + this.close(); } private _opened = false; diff --git a/projects/datetime-picker/src/lib/datepicker-input-base.ts b/projects/datetime-picker/src/lib/datepicker-input-base.ts index ca53f847..bfc64f6d 100644 --- a/projects/datetime-picker/src/lib/datepicker-input-base.ts +++ b/projects/datetime-picker/src/lib/datepicker-input-base.ts @@ -81,7 +81,7 @@ export abstract class NgxMatDatepickerInputBase | undefined; diff --git a/projects/datetime-picker/src/lib/datetime-picker-content-v2.component.ts b/projects/datetime-picker/src/lib/datetime-picker-content-v2.component.ts index 8054575b..f1e80e80 100644 --- a/projects/datetime-picker/src/lib/datetime-picker-content-v2.component.ts +++ b/projects/datetime-picker/src/lib/datetime-picker-content-v2.component.ts @@ -18,7 +18,6 @@ import { takeUntil } from 'rxjs/operators'; import { NgxMatTimepickerComponent } from './timepicker.component'; export interface NgxMatDatetimePickerContentData { - datepicker: any; // Reference to the main datepicker component color?: ThemePalette; touchUi?: boolean; hideTime?: boolean; diff --git a/projects/datetime-picker/src/lib/datetime-picker-input-v2.directive.ts b/projects/datetime-picker/src/lib/datetime-picker-input-v2.directive.ts index 6f1512c0..035a1519 100644 --- a/projects/datetime-picker/src/lib/datetime-picker-input-v2.directive.ts +++ b/projects/datetime-picker/src/lib/datetime-picker-input-v2.directive.ts @@ -8,6 +8,7 @@ import { OnDestroy, OnInit, Output, + Provider, forwardRef, inject, signal, @@ -30,13 +31,13 @@ import { NgxMatDatepickerControl } from './datepicker-base'; import { createMissingDateImplError } from './datepicker-errors'; import { NgxMatDatetimePickerV2 } from './datetime-picker-v2.component'; -export const NGX_MAT_DATETIME_PICKER_VALUE_ACCESSOR: any = { +export const NGX_MAT_DATETIME_PICKER_VALUE_ACCESSOR: Provider = { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => NgxMatDatetimePickerInputV2), multi: true, }; -export const NGX_MAT_DATETIME_PICKER_VALIDATORS: any = { +export const NGX_MAT_DATETIME_PICKER_VALIDATORS: Provider = { provide: NG_VALIDATORS, useExisting: forwardRef(() => NgxMatDatetimePickerInputV2), multi: true, @@ -340,7 +341,7 @@ export class NgxMatDatetimePickerInputV2 _onInput(event: Event): void { const target = event?.target as HTMLInputElement; const parsedDate = this._dateAdapter?.parse(target.value, this._dateFormats!.display.dateInput); - this._lastValueValid = this._dateAdapter?.isValid(parsedDate)!; + this._lastValueValid = this._dateAdapter ? this._dateAdapter.isValid(parsedDate) : false; const date = this._dateAdapter?.getValidDateOrNull(parsedDate); // Update internal value diff --git a/projects/datetime-picker/src/lib/timepicker.component.ts b/projects/datetime-picker/src/lib/timepicker.component.ts index 248ae22b..ab2b1db0 100644 --- a/projects/datetime-picker/src/lib/timepicker.component.ts +++ b/projects/datetime-picker/src/lib/timepicker.component.ts @@ -160,11 +160,11 @@ export class NgxMatTimepickerComponent implements ControlValueAccessor { } } - registerOnChange(fn: (_: any) => {}): void { + registerOnChange(fn: (_: any) => void): void { this._onChange = fn; } - registerOnTouched(fn: () => {}): void { + registerOnTouched(fn: () => void): void { this._onTouched = fn; }