Skip to content

Commit b472330

Browse files
mmalerbatinayuangao
authored andcommitted
docs(datepicker): add docs for custom calendar header (#10543)
* docs(datepicker): add docs for custom calendar header * fix error * fix error in example module generation
1 parent 0a50158 commit b472330

File tree

9 files changed

+186
-25
lines changed

9 files changed

+186
-25
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: 33 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +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 {DateAdapter, ThemePalette} from '@angular/material/core';
12-
import {MatCalendar, MatDatepickerInputEvent} from '@angular/material/datepicker';
13-
18+
import {MatCalendar} from '@angular/material';
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';
1423

1524
@Component({
1625
moduleId: module.id,
@@ -53,25 +62,38 @@ export class DatepickerDemo {
5362
styleUrls: ['custom-header.css'],
5463
changeDetection: ChangeDetectionStrategy.OnPush,
5564
})
56-
export class CustomHeader<D> {
65+
export class CustomHeader<D> implements OnDestroy {
66+
private _destroyed = new Subject<void>();
67+
5768
constructor(@Host() private _calendar: MatCalendar<D>,
58-
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+
}
5981

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

6688
previousClicked(mode: 'month' | 'year') {
6789
this._calendar.activeDate = mode == 'month' ?
6890
this._dateAdapter.addCalendarMonths(this._calendar.activeDate, -1) :
69-
this._dateAdapter.addCalendarYears(this._calendar.activeDate, -1);
91+
this._dateAdapter.addCalendarYears(this._calendar.activeDate, -1);
7092
}
7193

7294
nextClicked(mode: 'month' | 'year') {
7395
this._calendar.activeDate = mode == 'month' ?
7496
this._dateAdapter.addCalendarMonths(this._calendar.activeDate, 1) :
75-
this._dateAdapter.addCalendarYears(this._calendar.activeDate, 1);
97+
this._dateAdapter.addCalendarYears(this._calendar.activeDate, 1);
7698
}
7799
}

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: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
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';
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+
selector: 'example-header',
30+
template: `
31+
<div class="example-header">
32+
<button mat-icon-button class="example-double-arrow" (click)="previousClicked('year')">
33+
<mat-icon>keyboard_arrow_left</mat-icon>
34+
<mat-icon>keyboard_arrow_left</mat-icon>
35+
</button>
36+
<button mat-icon-button (click)="previousClicked('month')">
37+
<mat-icon>keyboard_arrow_left</mat-icon>
38+
</button>
39+
<span class="example-header-label">{{periodLabel}}</span>
40+
<button mat-icon-button (click)="nextClicked('month')">
41+
<mat-icon>keyboard_arrow_right</mat-icon>
42+
</button>
43+
<button mat-icon-button class="example-double-arrow" (click)="nextClicked('year')">
44+
<mat-icon>keyboard_arrow_right</mat-icon>
45+
<mat-icon>keyboard_arrow_right</mat-icon>
46+
</button>
47+
</div>
48+
`,
49+
encapsulation: ViewEncapsulation.None,
50+
changeDetection: ChangeDetectionStrategy.OnPush,
51+
})
52+
export class ExampleHeader<D> implements OnDestroy {
53+
private destroyed = new Subject<void>();
54+
55+
constructor(@Host() private calendar: MatCalendar<D>,
56+
private dateAdapter: DateAdapter<D>,
57+
@Inject(MAT_DATE_FORMATS) private dateFormats: MatDateFormats,
58+
cdr: ChangeDetectorRef) {
59+
calendar.stateChanges
60+
.pipe(takeUntil(this.destroyed))
61+
.subscribe(() => cdr.markForCheck());
62+
}
63+
64+
ngOnDestroy() {
65+
this.destroyed.next();
66+
this.destroyed.complete();
67+
}
68+
69+
get periodLabel() {
70+
return this.dateAdapter
71+
.format(this.calendar.activeDate, this.dateFormats.display.monthYearLabel)
72+
.toLocaleUpperCase();
73+
}
74+
75+
previousClicked(mode: 'month' | 'year') {
76+
this.calendar.activeDate = mode == 'month' ?
77+
this.dateAdapter.addCalendarMonths(this.calendar.activeDate, -1) :
78+
this.dateAdapter.addCalendarYears(this.calendar.activeDate, -1);
79+
}
80+
81+
nextClicked(mode: 'month' | 'year') {
82+
this.calendar.activeDate = mode == 'month' ?
83+
this.dateAdapter.addCalendarMonths(this.calendar.activeDate, 1) :
84+
this.dateAdapter.addCalendarYears(this.calendar.activeDate, 1);
85+
}
86+
}

tools/gulp/tasks/example-module.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,9 @@ task('build-examples-module', () => {
226226

227227
for (const meta of secondaryComponents) {
228228
example.additionalComponents.push(meta.component);
229-
example.additionalFiles.push(meta.templateUrl);
229+
if (meta.templateUrl) {
230+
example.additionalFiles.push(meta.templateUrl);
231+
}
230232
example.selectorName.push(meta.component);
231233
}
232234
}

0 commit comments

Comments
 (0)