Skip to content

Commit be17c25

Browse files
crisbetommalerba
authored andcommitted
fix(datepicker): re-render calendar when locale changes (#18094)
Fixes the different calendar views not being re-rendered when the locale is changed. This doesn't have much of an effect on the datepicker since the calendar is re-initialized every time, but it's useful when `mat-calendar` is consumed on its own. Fixes #18087.
1 parent 5fccd24 commit be17c25

File tree

5 files changed

+84
-9
lines changed

5 files changed

+84
-9
lines changed

src/material/datepicker/calendar.spec.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,45 @@ describe('MatCalendar', () => {
283283
});
284284
});
285285

286+
it('should re-render the month view when the locale changes',
287+
inject([DateAdapter], (adapter: DateAdapter<Date>) => {
288+
fixture.detectChanges();
289+
spyOn(calendarInstance.monthView, '_init').and.callThrough();
290+
291+
adapter.setLocale('bg-BG');
292+
fixture.detectChanges();
293+
294+
expect(calendarInstance.monthView._init).toHaveBeenCalled();
295+
}));
296+
297+
it('should re-render the year view when the locale changes',
298+
inject([DateAdapter], (adapter: DateAdapter<Date>) => {
299+
periodButton.click();
300+
fixture.detectChanges();
301+
302+
(calendarElement.querySelector('.mat-calendar-body-active') as HTMLElement).click();
303+
fixture.detectChanges();
304+
305+
spyOn(calendarInstance.yearView, '_init').and.callThrough();
306+
307+
adapter.setLocale('bg-BG');
308+
fixture.detectChanges();
309+
310+
expect(calendarInstance.yearView._init).toHaveBeenCalled();
311+
}));
312+
313+
it('should re-render the multi-year view when the locale changes',
314+
inject([DateAdapter], (adapter: DateAdapter<Date>) => {
315+
periodButton.click();
316+
fixture.detectChanges();
317+
318+
spyOn(calendarInstance.multiYearView, '_init').and.callThrough();
319+
320+
adapter.setLocale('bg-BG');
321+
fixture.detectChanges();
322+
323+
expect(calendarInstance.multiYearView._init).toHaveBeenCalled();
324+
}));
286325
});
287326

288327
describe('calendar with min and max date', () => {

src/material/datepicker/month-view.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,14 @@ import {
3030
Output,
3131
ViewEncapsulation,
3232
ViewChild,
33+
OnDestroy,
3334
} from '@angular/core';
3435
import {DateAdapter, MAT_DATE_FORMATS, MatDateFormats} from '@angular/material/core';
3536
import {Directionality} from '@angular/cdk/bidi';
3637
import {MatCalendarBody, MatCalendarCell, MatCalendarCellCssClasses} from './calendar-body';
3738
import {createMissingDateImplError} from './datepicker-errors';
39+
import {Subscription} from 'rxjs';
40+
import {startWith} from 'rxjs/operators';
3841

3942

4043
const DAYS_PER_WEEK = 7;
@@ -51,7 +54,9 @@ const DAYS_PER_WEEK = 7;
5154
encapsulation: ViewEncapsulation.None,
5255
changeDetection: ChangeDetectionStrategy.OnPush
5356
})
54-
export class MatMonthView<D> implements AfterContentInit {
57+
export class MatMonthView<D> implements AfterContentInit, OnDestroy {
58+
private _rerenderSubscription = Subscription.EMPTY;
59+
5560
/**
5661
* The date to display in this month view (everything other than the month and year is ignored).
5762
*/
@@ -147,7 +152,13 @@ export class MatMonthView<D> implements AfterContentInit {
147152
}
148153

149154
ngAfterContentInit() {
150-
this._init();
155+
this._rerenderSubscription = this._dateAdapter.localeChanges
156+
.pipe(startWith(null))
157+
.subscribe(() => this._init());
158+
}
159+
160+
ngOnDestroy() {
161+
this._rerenderSubscription.unsubscribe();
151162
}
152163

153164
/** Handles when a new date is selected. */

src/material/datepicker/multi-year-view.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,14 @@ import {
2929
Output,
3030
ViewChild,
3131
ViewEncapsulation,
32+
OnDestroy,
3233
} from '@angular/core';
3334
import {DateAdapter} from '@angular/material/core';
3435
import {Directionality} from '@angular/cdk/bidi';
3536
import {MatCalendarBody, MatCalendarCell} from './calendar-body';
3637
import {createMissingDateImplError} from './datepicker-errors';
38+
import {Subscription} from 'rxjs';
39+
import {startWith} from 'rxjs/operators';
3740

3841
export const yearsPerPage = 24;
3942

@@ -50,7 +53,9 @@ export const yearsPerRow = 4;
5053
encapsulation: ViewEncapsulation.None,
5154
changeDetection: ChangeDetectionStrategy.OnPush
5255
})
53-
export class MatMultiYearView<D> implements AfterContentInit {
56+
export class MatMultiYearView<D> implements AfterContentInit, OnDestroy {
57+
private _rerenderSubscription = Subscription.EMPTY;
58+
5459
/** The date to display in this multi-year view (everything other than the year is ignored). */
5560
@Input()
5661
get activeDate(): D { return this._activeDate; }
@@ -127,7 +132,13 @@ export class MatMultiYearView<D> implements AfterContentInit {
127132
}
128133

129134
ngAfterContentInit() {
130-
this._init();
135+
this._rerenderSubscription = this._dateAdapter.localeChanges
136+
.pipe(startWith(null))
137+
.subscribe(() => this._init());
138+
}
139+
140+
ngOnDestroy() {
141+
this._rerenderSubscription.unsubscribe();
131142
}
132143

133144
/** Initializes this multi-year view. */

src/material/datepicker/year-view.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,14 @@ import {
3030
Output,
3131
ViewChild,
3232
ViewEncapsulation,
33+
OnDestroy,
3334
} from '@angular/core';
3435
import {DateAdapter, MAT_DATE_FORMATS, MatDateFormats} from '@angular/material/core';
3536
import {Directionality} from '@angular/cdk/bidi';
3637
import {MatCalendarBody, MatCalendarCell} from './calendar-body';
3738
import {createMissingDateImplError} from './datepicker-errors';
39+
import {Subscription} from 'rxjs';
40+
import {startWith} from 'rxjs/operators';
3841

3942
/**
4043
* An internal component used to display a single year in the datepicker.
@@ -47,7 +50,9 @@ import {createMissingDateImplError} from './datepicker-errors';
4750
encapsulation: ViewEncapsulation.None,
4851
changeDetection: ChangeDetectionStrategy.OnPush
4952
})
50-
export class MatYearView<D> implements AfterContentInit {
53+
export class MatYearView<D> implements AfterContentInit, OnDestroy {
54+
private _rerenderSubscription = Subscription.EMPTY;
55+
5156
/** The date to display in this year view (everything other than the year is ignored). */
5257
@Input()
5358
get activeDate(): D { return this._activeDate; }
@@ -132,7 +137,13 @@ export class MatYearView<D> implements AfterContentInit {
132137
}
133138

134139
ngAfterContentInit() {
135-
this._init();
140+
this._rerenderSubscription = this._dateAdapter.localeChanges
141+
.pipe(startWith(null))
142+
.subscribe(() => this._init());
143+
}
144+
145+
ngOnDestroy() {
146+
this._rerenderSubscription.unsubscribe();
136147
}
137148

138149
/** Handles when a new month is selected. */

tools/public_api_guard/material/datepicker.d.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@ export declare class MatDatepickerToggleIcon {
244244
static ɵfac: i0.ɵɵFactoryDef<MatDatepickerToggleIcon>;
245245
}
246246

247-
export declare class MatMonthView<D> implements AfterContentInit {
247+
export declare class MatMonthView<D> implements AfterContentInit, OnDestroy {
248248
_dateAdapter: DateAdapter<D>;
249249
_firstWeekOffset: number;
250250
_matCalendarBody: MatCalendarBody;
@@ -271,11 +271,12 @@ export declare class MatMonthView<D> implements AfterContentInit {
271271
_handleCalendarBodyKeydown(event: KeyboardEvent): void;
272272
_init(): void;
273273
ngAfterContentInit(): void;
274+
ngOnDestroy(): void;
274275
static ɵcmp: i0.ɵɵComponentDefWithMeta<MatMonthView<any>, "mat-month-view", ["matMonthView"], { 'activeDate': "activeDate", 'selected': "selected", 'minDate': "minDate", 'maxDate': "maxDate", 'dateFilter': "dateFilter", 'dateClass': "dateClass" }, { 'selectedChange': "selectedChange", '_userSelection': "_userSelection", 'activeDateChange': "activeDateChange" }, never>;
275276
static ɵfac: i0.ɵɵFactoryDef<MatMonthView<any>>;
276277
}
277278

278-
export declare class MatMultiYearView<D> implements AfterContentInit {
279+
export declare class MatMultiYearView<D> implements AfterContentInit, OnDestroy {
279280
_dateAdapter: DateAdapter<D>;
280281
_matCalendarBody: MatCalendarBody;
281282
_selectedYear: number | null;
@@ -296,11 +297,12 @@ export declare class MatMultiYearView<D> implements AfterContentInit {
296297
_init(): void;
297298
_yearSelected(year: number): void;
298299
ngAfterContentInit(): void;
300+
ngOnDestroy(): void;
299301
static ɵcmp: i0.ɵɵComponentDefWithMeta<MatMultiYearView<any>, "mat-multi-year-view", ["matMultiYearView"], { 'activeDate': "activeDate", 'selected': "selected", 'minDate': "minDate", 'maxDate': "maxDate", 'dateFilter': "dateFilter" }, { 'selectedChange': "selectedChange", 'yearSelected': "yearSelected", 'activeDateChange': "activeDateChange" }, never>;
300302
static ɵfac: i0.ɵɵFactoryDef<MatMultiYearView<any>>;
301303
}
302304

303-
export declare class MatYearView<D> implements AfterContentInit {
305+
export declare class MatYearView<D> implements AfterContentInit, OnDestroy {
304306
_dateAdapter: DateAdapter<D>;
305307
_matCalendarBody: MatCalendarBody;
306308
_months: MatCalendarCell[][];
@@ -321,6 +323,7 @@ export declare class MatYearView<D> implements AfterContentInit {
321323
_init(): void;
322324
_monthSelected(month: number): void;
323325
ngAfterContentInit(): void;
326+
ngOnDestroy(): void;
324327
static ɵcmp: i0.ɵɵComponentDefWithMeta<MatYearView<any>, "mat-year-view", ["matYearView"], { 'activeDate': "activeDate", 'selected': "selected", 'minDate': "minDate", 'maxDate': "maxDate", 'dateFilter': "dateFilter" }, { 'selectedChange': "selectedChange", 'monthSelected': "monthSelected", 'activeDateChange': "activeDateChange" }, never>;
325328
static ɵfac: i0.ɵɵFactoryDef<MatYearView<any>>;
326329
}

0 commit comments

Comments
 (0)