Skip to content

Commit

Permalink
fix(material/datepicker): actions not re-rendering if swapped out whi…
Browse files Browse the repository at this point in the history
…le calendar is open (#25123)

Fixes that we were only refreshing the actions while the calendar is closed.

Fixes #25122.

(cherry picked from commit 7d87068)
  • Loading branch information
crisbeto committed Jun 22, 2022
1 parent 7a0753c commit 8df5efe
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 5 deletions.
22 changes: 22 additions & 0 deletions src/material/datepicker/datepicker-actions.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,28 @@ describe('MatDatepickerActions', () => {
expect(control.value).toBeTruthy();
expect(onDateChange).toHaveBeenCalledTimes(1);
}));

it('should be able to toggle the actions while the datepicker is open', fakeAsync(() => {
const fixture = createComponent(DatepickerWithActions);
fixture.componentInstance.renderActions = false;
fixture.detectChanges();

fixture.componentInstance.datepicker.open();
fixture.detectChanges();
tick();
flush();

const content = document.querySelector('.mat-datepicker-content')!;
expect(content.querySelector('.mat-datepicker-actions')).toBeFalsy();

fixture.componentInstance.renderActions = true;
fixture.detectChanges();
expect(content.querySelector('.mat-datepicker-actions')).toBeTruthy();

fixture.componentInstance.renderActions = false;
fixture.detectChanges();
expect(content.querySelector('.mat-datepicker-actions')).toBeFalsy();
}));
});

@Component({
Expand Down
27 changes: 22 additions & 5 deletions src/material/datepicker/datepicker-base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -180,10 +180,6 @@ export class MatDatepickerContent<S, D = ExtractDateTypeFromSelection<S>>
}

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

Expand Down Expand Up @@ -246,6 +242,25 @@ export class MatDatepickerContent<S, D = ExtractDateTypeFromSelection<S>>
this._globalModel.updateSelection(this._model.selection, this);
}
}

/**
* Assigns a new portal containing the datepicker actions.
* @param portal Portal with the actions to be assigned.
* @param forceRerender Whether a re-render of the portal should be triggered. This isn't
* necessary if the portal is assigned during initialization, but it may be required if it's
* added at a later point.
*/
_assignActions(portal: TemplatePortal<any> | null, forceRerender: boolean) {
// If we have actions, clone the model so that we have the ability to cancel the selection,
// otherwise update the global model directly. Note that we want to assign this as soon as
// possible, but `_actionsPortal` isn't available in the constructor so we do it in `ngOnInit`.
this._model = portal ? this._globalModel.clone() : this._globalModel;
this._actionsPortal = portal;

if (forceRerender) {
this._changeDetectorRef.detectChanges();
}
}
}

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

/**
Expand All @@ -565,6 +581,7 @@ export abstract class MatDatepickerBase<
removeActions(portal: TemplatePortal): void {
if (portal === this._actionsPortal) {
this._actionsPortal = null;
this._componentRef?.instance._assignActions(null, true);
}
}

Expand Down Expand Up @@ -632,8 +649,8 @@ export abstract class MatDatepickerBase<
protected _forwardContentValues(instance: MatDatepickerContent<S, D>) {
instance.datepicker = this;
instance.color = this.color;
instance._actionsPortal = this._actionsPortal;
instance._dialogLabelId = this.datepickerInput.getOverlayLabelId();
instance._assignActions(this._actionsPortal, false);
}

/** Opens the overlay with the calendar. */
Expand Down
1 change: 1 addition & 0 deletions tools/public_api_guard/material/datepicker.md
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,7 @@ export class MatDatepickerContent<S, D = ExtractDateTypeFromSelection<S>> extend
readonly _animationDone: Subject<void>;
_animationState: 'enter-dropdown' | 'enter-dialog' | 'void';
_applyPendingSelection(): void;
_assignActions(portal: TemplatePortal<any> | null, forceRerender: boolean): void;
_calendar: MatCalendar<D>;
_closeButtonFocused: boolean;
_closeButtonText: string;
Expand Down

0 comments on commit 8df5efe

Please sign in to comment.