Skip to content

Commit b12279c

Browse files
committed
docs(datepicker): add docs for custom calendar header
1 parent c05005a commit b12279c

File tree

8 files changed

+183
-24
lines changed

8 files changed

+183
-24
lines changed
Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,17 @@
1-
<div class="custom-header">
2-
<button mat-icon-button (click)="previousClicked('year')">&lt;&lt;</button>
3-
<button mat-icon-button (click)="previousClicked('month')">&lt;</button>
4-
<span class="custom-header-label">{{periodLabel}}</span>
5-
<button mat-icon-button (click)="nextClicked('month')">&gt;</button>
6-
<button mat-icon-button (click)="nextClicked('year')">&gt;&gt;</button>
1+
<div class="demo-calendar-header">
2+
<button mat-icon-button class="demo-double-arrow" (click)="previousClicked('year')">
3+
<mat-icon>keyboard_arrow_left</mat-icon>
4+
<mat-icon>keyboard_arrow_left</mat-icon>
5+
</button>
6+
<button mat-icon-button (click)="previousClicked('month')">
7+
<mat-icon>keyboard_arrow_left</mat-icon>
8+
</button>
9+
<span class="demo-calendar-header-label">{{periodLabel}}</span>
10+
<button mat-icon-button (click)="nextClicked('month')">
11+
<mat-icon>keyboard_arrow_right</mat-icon>
12+
</button>
13+
<button mat-icon-button class="demo-double-arrow" (click)="nextClicked('year')">
14+
<mat-icon>keyboard_arrow_right</mat-icon>
15+
<mat-icon>keyboard_arrow_right</mat-icon>
16+
</button>
717
</div>
Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
1-
.custom-header {
2-
padding: 1em 1.5em;
1+
.demo-calendar-header {
32
display: flex;
43
align-items: center;
4+
padding: 0.5em;
55
}
66

7-
.custom-header-label {
7+
.demo-calendar-header-label {
88
flex: 1;
9+
height: 1em;
10+
font-weight: bold;
911
text-align: center;
1012
}
13+
14+
.demo-double-arrow .mat-icon {
15+
margin: -22%;
16+
}

src/demo-app/datepicker/datepicker-demo.html

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -149,9 +149,8 @@ <h2>Datepicker with custom header</h2>
149149
<p>
150150
<mat-form-field>
151151
<mat-label>Custom calendar header</mat-label>
152-
<input matInput [matDatepicker]="customCalendarHeaderPicker"
153-
[disabled]="inputDisabled">
154-
<mat-datepicker-toggle matSuffix [for]="customCalendarHeaderPicker"></mat-datepicker-toggle>
155-
<mat-datepicker #customCalendarHeaderPicker [touchUi]="touch" [disabled]="datepickerDisabled" [calendarHeaderComponent]="customHeader"></mat-datepicker>
152+
<input matInput [matDatepicker]="customHeaderPicker">
153+
<mat-datepicker-toggle matSuffix [for]="customHeaderPicker"></mat-datepicker-toggle>
154+
<mat-datepicker #customHeaderPicker [calendarHeaderComponent]="customHeader"></mat-datepicker>
156155
</mat-form-field>
157156
</p>

src/demo-app/datepicker/datepicker-demo.ts

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,20 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9-
import {ChangeDetectionStrategy, Component, Host} from '@angular/core';
9+
import {
10+
ChangeDetectionStrategy,
11+
ChangeDetectorRef,
12+
Component,
13+
Host,
14+
Inject,
15+
OnDestroy
16+
} from '@angular/core';
1017
import {FormControl} from '@angular/forms';
11-
import {MatDatepickerInputEvent} from '@angular/material/datepicker';
12-
import {DateAdapter} from '@angular/material/core';
1318
import {MatCalendar} from '@angular/material';
14-
import {ThemePalette} from '@angular/material/core';
19+
import {DateAdapter, MAT_DATE_FORMATS, MatDateFormats, ThemePalette} from '@angular/material/core';
20+
import {MatDatepickerInputEvent} from '@angular/material/datepicker';
21+
import {Subject} from 'rxjs';
22+
import {takeUntil} from 'rxjs/operators';
1523

1624
@Component({
1725
moduleId: module.id,
@@ -54,25 +62,38 @@ export class DatepickerDemo {
5462
styleUrls: ['custom-header.css'],
5563
changeDetection: ChangeDetectionStrategy.OnPush,
5664
})
57-
export class CustomHeader<D> {
65+
export class CustomHeader<D> implements OnDestroy {
66+
private _destroyed = new Subject<void>();
67+
5868
constructor(@Host() private _calendar: MatCalendar<D>,
59-
private _dateAdapter: DateAdapter<D>) {}
69+
private _dateAdapter: DateAdapter<D>,
70+
@Inject(MAT_DATE_FORMATS) private _dateFormats: MatDateFormats,
71+
cdr: ChangeDetectorRef) {
72+
_calendar.stateChanges
73+
.pipe(takeUntil(this._destroyed))
74+
.subscribe(() => cdr.markForCheck());
75+
}
76+
77+
ngOnDestroy() {
78+
this._destroyed.next();
79+
this._destroyed.complete();
80+
}
6081

6182
get periodLabel() {
62-
const year = this._dateAdapter.getYearName(this._calendar.activeDate);
63-
const month = (this._dateAdapter.getMonth(this._calendar.activeDate) + 1);
64-
return `${month}/${year}`;
83+
return this._dateAdapter
84+
.format(this._calendar.activeDate, this._dateFormats.display.monthYearLabel)
85+
.toLocaleUpperCase();
6586
}
6687

6788
previousClicked(mode: 'month' | 'year') {
6889
this._calendar.activeDate = mode == 'month' ?
6990
this._dateAdapter.addCalendarMonths(this._calendar.activeDate, -1) :
70-
this._dateAdapter.addCalendarYears(this._calendar.activeDate, -1);
91+
this._dateAdapter.addCalendarYears(this._calendar.activeDate, -1);
7192
}
7293

7394
nextClicked(mode: 'month' | 'year') {
7495
this._calendar.activeDate = mode == 'month' ?
7596
this._dateAdapter.addCalendarMonths(this._calendar.activeDate, 1) :
76-
this._dateAdapter.addCalendarYears(this._calendar.activeDate, 1);
97+
this._dateAdapter.addCalendarYears(this._calendar.activeDate, 1);
7798
}
7899
}

src/lib/datepicker/datepicker.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,20 @@ export class MyApp {}
287287

288288
<!-- example(datepicker-formats) -->
289289

290+
#### Customizing the calendar header
291+
292+
The header section of the calendar (the part containing the view switcher and previous and next
293+
buttons) can be replaced with a custom component if desired. This is accomplished using the
294+
`calendarHeaderComponent` property of `<mat-datepicker>`. It takes a component class and constructs
295+
an instance of the component to use as the header.
296+
297+
In order to interact with the calendar in your custom header component, you can inject the parent
298+
`MatCalendar` in the constructor. To make sure your header stays in sync with the calendar,
299+
subscribe to the `stateChanges` observable of the calendar and mark your header component for change
300+
detection.
301+
302+
<!-- example(datepicker-custom-header) -->
303+
290304
#### Localizing labels and messages
291305

292306
The various text strings used by the datepicker are provided through `MatDatepickerIntl`.
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
.example-header {
2+
display: flex;
3+
align-items: center;
4+
padding: 0.5em;
5+
}
6+
7+
.example-header-label {
8+
flex: 1;
9+
height: 1em;
10+
font-weight: bold;
11+
text-align: center;
12+
}
13+
14+
.example-double-arrow .mat-icon {
15+
margin: -22%;
16+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<mat-form-field>
2+
<mat-label>Custom calendar header</mat-label>
3+
<input matInput [matDatepicker]="picker">
4+
<mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
5+
<mat-datepicker #picker [calendarHeaderComponent]="exampleHeader"></mat-datepicker>
6+
</mat-form-field>
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import {
2+
ChangeDetectionStrategy,
3+
ChangeDetectorRef,
4+
Component,
5+
Host,
6+
Inject,
7+
OnDestroy,
8+
ViewEncapsulation
9+
} from '@angular/core';
10+
import {MatCalendar} from '@angular/material';
11+
import {DateAdapter, MAT_DATE_FORMATS, MatDateFormats} from '@angular/material/core';
12+
import {Subject} from 'rxjs/index';
13+
import {takeUntil} from 'rxjs/operators';
14+
15+
/** @title Datepicker with custom calendar header */
16+
@Component({
17+
selector: 'datepicker-custom-header-example',
18+
templateUrl: 'datepicker-custom-header-example.html',
19+
styleUrls: ['datepicker-custom-header-example.css'],
20+
encapsulation: ViewEncapsulation.None,
21+
changeDetection: ChangeDetectionStrategy.OnPush,
22+
})
23+
export class DatepickerCustomHeaderExample {
24+
exampleHeader = ExampleHeader;
25+
}
26+
27+
/** Custom header component for datepicker. */
28+
@Component({
29+
moduleId: module.id,
30+
selector: 'example-header',
31+
template: `
32+
<div class="example-header">
33+
<button mat-icon-button class="example-double-arrow" (click)="previousClicked('year')">
34+
<mat-icon>keyboard_arrow_left</mat-icon>
35+
<mat-icon>keyboard_arrow_left</mat-icon>
36+
</button>
37+
<button mat-icon-button (click)="previousClicked('month')">
38+
<mat-icon>keyboard_arrow_left</mat-icon>
39+
</button>
40+
<span class="example-header-label">{{periodLabel}}</span>
41+
<button mat-icon-button (click)="nextClicked('month')">
42+
<mat-icon>keyboard_arrow_right</mat-icon>
43+
</button>
44+
<button mat-icon-button class="example-double-arrow" (click)="nextClicked('year')">
45+
<mat-icon>keyboard_arrow_right</mat-icon>
46+
<mat-icon>keyboard_arrow_right</mat-icon>
47+
</button>
48+
</div>
49+
`,
50+
encapsulation: ViewEncapsulation.None,
51+
changeDetection: ChangeDetectionStrategy.OnPush,
52+
})
53+
export class ExampleHeader<D> implements OnDestroy {
54+
private destroyed = new Subject<void>();
55+
56+
constructor(@Host() private calendar: MatCalendar<D>,
57+
private dateAdapter: DateAdapter<D>,
58+
@Inject(MAT_DATE_FORMATS) private dateFormats: MatDateFormats,
59+
cdr: ChangeDetectorRef) {
60+
calendar.stateChanges
61+
.pipe(takeUntil(this.destroyed))
62+
.subscribe(() => cdr.markForCheck());
63+
}
64+
65+
ngOnDestroy() {
66+
this.destroyed.next();
67+
this.destroyed.complete();
68+
}
69+
70+
get periodLabel() {
71+
return this.dateAdapter
72+
.format(this.calendar.activeDate, this.dateFormats.display.monthYearLabel)
73+
.toLocaleUpperCase();
74+
}
75+
76+
previousClicked(mode: 'month' | 'year') {
77+
this.calendar.activeDate = mode == 'month' ?
78+
this.dateAdapter.addCalendarMonths(this.calendar.activeDate, -1) :
79+
this.dateAdapter.addCalendarYears(this.calendar.activeDate, -1);
80+
}
81+
82+
nextClicked(mode: 'month' | 'year') {
83+
this.calendar.activeDate = mode == 'month' ?
84+
this.dateAdapter.addCalendarMonths(this.calendar.activeDate, 1) :
85+
this.dateAdapter.addCalendarYears(this.calendar.activeDate, 1);
86+
}
87+
}

0 commit comments

Comments
 (0)