Skip to content

Commit 4e04540

Browse files
authored
fix(material/datepicker): simplify DI setup (#30247)
Previously we had to define an injection token for the `MatDateRangeInput` in order to avoid circular references. Now we can do the same with a type-only imports so we can simplify the setup. Fixes #30238.
1 parent 2be0afc commit 4e04540

File tree

3 files changed

+30
-51
lines changed

3 files changed

+30
-51
lines changed

src/material/datepicker/date-range-input-parts.ts

Lines changed: 19 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,13 @@
99
import {Directionality} from '@angular/cdk/bidi';
1010
import {BACKSPACE, LEFT_ARROW, RIGHT_ARROW} from '@angular/cdk/keycodes';
1111
import {
12+
AfterContentInit,
1213
Directive,
1314
DoCheck,
1415
ElementRef,
15-
InjectionToken,
1616
Injector,
1717
Input,
1818
OnInit,
19-
Signal,
2019
inject,
2120
} from '@angular/core';
2221
import {
@@ -33,44 +32,18 @@ import {
3332
import {ErrorStateMatcher, _ErrorStateTracker} from '@angular/material/core';
3433
import {_computeAriaAccessibleName} from './aria-accessible-name';
3534
import {DateRange, DateSelectionModelChange} from './date-selection-model';
36-
import {DateFilterFn, MatDatepickerInputBase} from './datepicker-input-base';
37-
38-
/** Parent component that should be wrapped around `MatStartDate` and `MatEndDate`. */
39-
export interface MatDateRangeInputParent<D> {
40-
id: string;
41-
min: D | null;
42-
max: D | null;
43-
dateFilter: DateFilterFn<D>;
44-
rangePicker: {
45-
opened: boolean;
46-
id: string;
47-
};
48-
// @breaking-change 20.0.0 property to become required.
49-
_ariaOwns?: Signal<string | null>;
50-
_startInput: MatDateRangeInputPartBase<D>;
51-
_endInput: MatDateRangeInputPartBase<D>;
52-
_groupDisabled: boolean;
53-
_handleChildValueChange(): void;
54-
_openDatepicker(): void;
55-
}
56-
57-
/**
58-
* Used to provide the date range input wrapper component
59-
* to the parts without circular dependencies.
60-
*/
61-
export const MAT_DATE_RANGE_INPUT_PARENT = new InjectionToken<MatDateRangeInputParent<unknown>>(
62-
'MAT_DATE_RANGE_INPUT_PARENT',
63-
);
35+
import {MatDatepickerInputBase} from './datepicker-input-base';
36+
import {MatDateRangeInput} from './date-range-input';
6437

6538
/**
6639
* Base class for the individual inputs that can be projected inside a `mat-date-range-input`.
6740
*/
6841
@Directive()
6942
abstract class MatDateRangeInputPartBase<D>
7043
extends MatDatepickerInputBase<DateRange<D>>
71-
implements OnInit, DoCheck
44+
implements OnInit, AfterContentInit, DoCheck
7245
{
73-
_rangeInput = inject<MatDateRangeInputParent<D>>(MAT_DATE_RANGE_INPUT_PARENT);
46+
_rangeInput = inject<MatDateRangeInput<D>>(MatDateRangeInput);
7447
override _elementRef = inject<ElementRef<HTMLInputElement>>(ElementRef);
7548
_defaultErrorStateMatcher = inject(ErrorStateMatcher);
7649
private _injector = inject(Injector);
@@ -86,6 +59,7 @@ abstract class MatDateRangeInputPartBase<D>
8659
protected abstract override _validator: ValidatorFn | null;
8760
protected abstract override _assignValueToModel(value: D | null): void;
8861
protected abstract override _getValueFromModel(modelValue: DateRange<D>): D | null;
62+
protected abstract _register(): void;
8963
protected readonly _dir = inject(Directionality, {optional: true});
9064
private _errorStateTracker: _ErrorStateTracker;
9165

@@ -135,6 +109,10 @@ abstract class MatDateRangeInputPartBase<D>
135109
}
136110
}
137111

112+
ngAfterContentInit(): void {
113+
this._register();
114+
}
115+
138116
ngDoCheck() {
139117
if (this.ngControl) {
140118
// We need to re-evaluate this on every change detection cycle, because there are some
@@ -208,7 +186,7 @@ abstract class MatDateRangeInputPartBase<D>
208186
protected override _assignValueProgrammatically(value: D | null) {
209187
super._assignValueProgrammatically(value);
210188
const opposite = (
211-
this === this._rangeInput._startInput
189+
this === (this._rangeInput._startInput as MatDateRangeInputPartBase<D>)
212190
? this._rangeInput._endInput
213191
: this._rangeInput._startInput
214192
) as MatDateRangeInputPartBase<D> | undefined;
@@ -261,6 +239,10 @@ export class MatStartDate<D> extends MatDateRangeInputPartBase<D> {
261239

262240
protected _validator = Validators.compose([...super._getValidators(), this._startValidator]);
263241

242+
protected override _register(): void {
243+
this._rangeInput._startInput = this;
244+
}
245+
264246
protected _getValueFromModel(modelValue: DateRange<D>) {
265247
return modelValue.start;
266248
}
@@ -349,6 +331,10 @@ export class MatEndDate<D> extends MatDateRangeInputPartBase<D> {
349331
: {'matEndDateInvalid': {'start': start, 'actual': end}};
350332
};
351333

334+
protected override _register(): void {
335+
this._rangeInput._endInput = this;
336+
}
337+
352338
protected _validator = Validators.compose([...super._getValidators(), this._endValidator]);
353339

354340
protected _getValueFromModel(modelValue: DateRange<D>) {

src/material/datepicker/date-range-input.ts

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import {
1212
ChangeDetectionStrategy,
1313
ChangeDetectorRef,
1414
Component,
15-
ContentChild,
1615
ElementRef,
1716
Input,
1817
OnChanges,
@@ -27,12 +26,7 @@ import {ControlContainer, NgControl, Validators} from '@angular/forms';
2726
import {DateAdapter, ThemePalette} from '@angular/material/core';
2827
import {MAT_FORM_FIELD, MatFormFieldControl} from '@angular/material/form-field';
2928
import {Subject, Subscription, merge} from 'rxjs';
30-
import {
31-
MAT_DATE_RANGE_INPUT_PARENT,
32-
MatDateRangeInputParent,
33-
MatEndDate,
34-
MatStartDate,
35-
} from './date-range-input-parts';
29+
import type {MatEndDate, MatStartDate} from './date-range-input-parts';
3630
import {MatDateRangePickerInput} from './date-range-picker';
3731
import {DateRange, MatDateSelectionModel} from './date-selection-model';
3832
import {MatDatepickerControl, MatDatepickerPanel} from './datepicker-base';
@@ -58,17 +52,13 @@ import {DateFilterFn, _MatFormFieldPartial, dateInputsHaveChanged} from './datep
5852
},
5953
changeDetection: ChangeDetectionStrategy.OnPush,
6054
encapsulation: ViewEncapsulation.None,
61-
providers: [
62-
{provide: MatFormFieldControl, useExisting: MatDateRangeInput},
63-
{provide: MAT_DATE_RANGE_INPUT_PARENT, useExisting: MatDateRangeInput},
64-
],
55+
providers: [{provide: MatFormFieldControl, useExisting: MatDateRangeInput}],
6556
imports: [CdkMonitorFocus],
6657
})
6758
export class MatDateRangeInput<D>
6859
implements
6960
MatFormFieldControl<DateRange<D>>,
7061
MatDatepickerControl<D>,
71-
MatDateRangeInputParent<D>,
7262
MatDateRangePickerInput<D>,
7363
AfterContentInit,
7464
OnChanges,
@@ -82,6 +72,9 @@ export class MatDateRangeInput<D>
8272
private _closedSubscription = Subscription.EMPTY;
8373
private _openedSubscription = Subscription.EMPTY;
8474

75+
_startInput: MatStartDate<D>;
76+
_endInput: MatEndDate<D>;
77+
8578
/** Current value of the range input. */
8679
get value() {
8780
return this._model ? this._model.selection : null;
@@ -254,9 +247,6 @@ export class MatDateRangeInput<D>
254247
/** End of the comparison range that should be shown in the calendar. */
255248
@Input() comparisonEnd: D | null = null;
256249

257-
@ContentChild(MatStartDate) _startInput: MatStartDate<D>;
258-
@ContentChild(MatEndDate) _endInput: MatEndDate<D>;
259-
260250
/**
261251
* Implemented as a part of `MatFormFieldControl`.
262252
* TODO(crisbeto): change type to `AbstractControlDirective` after #18206 lands.

tools/public_api_guard/material/datepicker.md

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ import { OnInit } from '@angular/core';
4141
import { Overlay } from '@angular/cdk/overlay';
4242
import { Portal } from '@angular/cdk/portal';
4343
import { ScrollStrategy } from '@angular/cdk/overlay';
44-
import { Signal } from '@angular/core';
4544
import { SimpleChanges } from '@angular/core';
4645
import { Subject } from 'rxjs';
4746
import { TemplatePortal } from '@angular/cdk/portal';
@@ -568,7 +567,7 @@ export class MatDatepickerToggleIcon {
568567
}
569568

570569
// @public (undocumented)
571-
export class MatDateRangeInput<D> implements MatFormFieldControl<DateRange<D>>, MatDatepickerControl<D>, MatDateRangeInputParent<D>, MatDateRangePickerInput<D>, AfterContentInit, OnChanges, OnDestroy {
570+
export class MatDateRangeInput<D> implements MatFormFieldControl<DateRange<D>>, MatDatepickerControl<D>, MatDateRangePickerInput<D>, AfterContentInit, OnChanges, OnDestroy {
572571
constructor(...args: unknown[]);
573572
_ariaDescribedBy: string | null;
574573
_ariaOwns: WritableSignal<string | null>;
@@ -632,7 +631,7 @@ export class MatDateRangeInput<D> implements MatFormFieldControl<DateRange<D>>,
632631
_updateFocus(origin: FocusOrigin): void;
633632
get value(): DateRange<D> | null;
634633
// (undocumented)
635-
static ɵcmp: i0.ɵɵComponentDeclaration<MatDateRangeInput<any>, "mat-date-range-input", ["matDateRangeInput"], { "rangePicker": { "alias": "rangePicker"; "required": false; }; "required": { "alias": "required"; "required": false; }; "dateFilter": { "alias": "dateFilter"; "required": false; }; "min": { "alias": "min"; "required": false; }; "max": { "alias": "max"; "required": false; }; "disabled": { "alias": "disabled"; "required": false; }; "separator": { "alias": "separator"; "required": false; }; "comparisonStart": { "alias": "comparisonStart"; "required": false; }; "comparisonEnd": { "alias": "comparisonEnd"; "required": false; }; }, {}, ["_startInput", "_endInput"], ["input[matStartDate]", "input[matEndDate]"], true, never>;
634+
static ɵcmp: i0.ɵɵComponentDeclaration<MatDateRangeInput<any>, "mat-date-range-input", ["matDateRangeInput"], { "rangePicker": { "alias": "rangePicker"; "required": false; }; "required": { "alias": "required"; "required": false; }; "dateFilter": { "alias": "dateFilter"; "required": false; }; "min": { "alias": "min"; "required": false; }; "max": { "alias": "max"; "required": false; }; "disabled": { "alias": "disabled"; "required": false; }; "separator": { "alias": "separator"; "required": false; }; "comparisonStart": { "alias": "comparisonStart"; "required": false; }; "comparisonEnd": { "alias": "comparisonEnd"; "required": false; }; }, {}, never, ["input[matStartDate]", "input[matEndDate]"], true, never>;
636635
// (undocumented)
637636
static ɵfac: i0.ɵɵFactoryDeclaration<MatDateRangeInput<any>, never>;
638637
}
@@ -686,6 +685,8 @@ export class MatEndDate<D> extends MatDateRangeInputPartBase<D> {
686685
// (undocumented)
687686
_onKeydown(event: KeyboardEvent): void;
688687
// (undocumented)
688+
protected _register(): void;
689+
// (undocumented)
689690
protected _shouldHandleChangeEvent(change: DateSelectionModelChange<DateRange<D>>): boolean;
690691
// (undocumented)
691692
protected _validator: ValidatorFn | null;
@@ -838,6 +839,8 @@ export class MatStartDate<D> extends MatDateRangeInputPartBase<D> {
838839
// (undocumented)
839840
_onKeydown(event: KeyboardEvent): void;
840841
// (undocumented)
842+
protected _register(): void;
843+
// (undocumented)
841844
protected _shouldHandleChangeEvent(change: DateSelectionModelChange<DateRange<D>>): boolean;
842845
// (undocumented)
843846
protected _validator: ValidatorFn | null;

0 commit comments

Comments
 (0)