Skip to content

Commit 751da90

Browse files
committed
fix(material/datepicker): actions not re-rendering if swapped out while calendar is open
Fixes that we were only refreshing the actions while the calendar is closed. Fixes #25122.
1 parent 1f22b09 commit 751da90

File tree

3 files changed

+45
-5
lines changed

3 files changed

+45
-5
lines changed

src/material/datepicker/datepicker-actions.spec.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,28 @@ describe('MatDatepickerActions', () => {
257257
expect(control.value).toBeTruthy();
258258
expect(onDateChange).toHaveBeenCalledTimes(1);
259259
}));
260+
261+
it('should be able to toggle the actions while the datepicker is open', fakeAsync(() => {
262+
const fixture = createComponent(DatepickerWithActions);
263+
fixture.componentInstance.renderActions = false;
264+
fixture.detectChanges();
265+
266+
fixture.componentInstance.datepicker.open();
267+
fixture.detectChanges();
268+
tick();
269+
flush();
270+
271+
const content = document.querySelector('.mat-datepicker-content')!;
272+
expect(content.querySelector('.mat-datepicker-actions')).toBeFalsy();
273+
274+
fixture.componentInstance.renderActions = true;
275+
fixture.detectChanges();
276+
expect(content.querySelector('.mat-datepicker-actions')).toBeTruthy();
277+
278+
fixture.componentInstance.renderActions = false;
279+
fixture.detectChanges();
280+
expect(content.querySelector('.mat-datepicker-actions')).toBeFalsy();
281+
}));
260282
});
261283

262284
@Component({

src/material/datepicker/datepicker-base.ts

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -180,10 +180,6 @@ export class MatDatepickerContent<S, D = ExtractDateTypeFromSelection<S>>
180180
}
181181

182182
ngOnInit() {
183-
// If we have actions, clone the model so that we have the ability to cancel the selection,
184-
// otherwise update the global model directly. Note that we want to assign this as soon as
185-
// possible, but `_actionsPortal` isn't available in the constructor so we do it in `ngOnInit`.
186-
this._model = this._actionsPortal ? this._globalModel.clone() : this._globalModel;
187183
this._animationState = this.datepicker.touchUi ? 'enter-dialog' : 'enter-dropdown';
188184
}
189185

@@ -246,6 +242,25 @@ export class MatDatepickerContent<S, D = ExtractDateTypeFromSelection<S>>
246242
this._globalModel.updateSelection(this._model.selection, this);
247243
}
248244
}
245+
246+
/**
247+
* Assigns a new portal containing the datepicker actions.
248+
* @param portal Portal with the actions to be assigned.
249+
* @param forceRerender Whether a re-render of the portal should be triggered. This isn't
250+
* necessary if the portal is assigned during initialization, but it may be required if it's
251+
* added at a later point.
252+
*/
253+
_assignActions(portal: TemplatePortal<any> | null, forceRerender: boolean) {
254+
// If we have actions, clone the model so that we have the ability to cancel the selection,
255+
// otherwise update the global model directly. Note that we want to assign this as soon as
256+
// possible, but `_actionsPortal` isn't available in the constructor so we do it in `ngOnInit`.
257+
this._model = portal ? this._globalModel.clone() : this._globalModel;
258+
this._actionsPortal = portal;
259+
260+
if (forceRerender) {
261+
this._changeDetectorRef.detectChanges();
262+
}
263+
}
249264
}
250265

251266
/** Form control that can be associated with a datepicker. */
@@ -556,6 +571,7 @@ export abstract class MatDatepickerBase<
556571
throw Error('A MatDatepicker can only be associated with a single actions row.');
557572
}
558573
this._actionsPortal = portal;
574+
this._componentRef?.instance._assignActions(portal, true);
559575
}
560576

561577
/**
@@ -565,6 +581,7 @@ export abstract class MatDatepickerBase<
565581
removeActions(portal: TemplatePortal): void {
566582
if (portal === this._actionsPortal) {
567583
this._actionsPortal = null;
584+
this._componentRef?.instance._assignActions(null, true);
568585
}
569586
}
570587

@@ -632,8 +649,8 @@ export abstract class MatDatepickerBase<
632649
protected _forwardContentValues(instance: MatDatepickerContent<S, D>) {
633650
instance.datepicker = this;
634651
instance.color = this.color;
635-
instance._actionsPortal = this._actionsPortal;
636652
instance._dialogLabelId = this.datepickerInput.getOverlayLabelId();
653+
instance._assignActions(this._actionsPortal, false);
637654
}
638655

639656
/** Opens the overlay with the calendar. */

tools/public_api_guard/material/datepicker.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -427,6 +427,7 @@ export class MatDatepickerContent<S, D = ExtractDateTypeFromSelection<S>> extend
427427
readonly _animationDone: Subject<void>;
428428
_animationState: 'enter-dropdown' | 'enter-dialog' | 'void';
429429
_applyPendingSelection(): void;
430+
_assignActions(portal: TemplatePortal<any> | null, forceRerender: boolean): void;
430431
_calendar: MatCalendar<D>;
431432
_closeButtonFocused: boolean;
432433
_closeButtonText: string;

0 commit comments

Comments
 (0)