Skip to content

Commit 206d9e0

Browse files
mmalerbatinayuangao
authored andcommitted
docs(datepicker): update docs and add examples (#7837)
* make some tweaks to datepicker docs * add examples * fix e2e * address comments
1 parent d815ebf commit 206d9e0

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+443
-96
lines changed

src/demo-app/system-config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ System.config({
2929

3030
// TODO(devversion): replace once the index.ts file for the Material package has been added.
3131
'@angular/material': 'dist/packages/material/public-api.js',
32+
'@angular/material-moment-adapter': 'dist/packages/material-moment-adapter/public-api.js',
3233
'@angular/cdk': 'dist/packages/cdk/index.js',
3334
'@angular/cdk/a11y': 'dist/packages/cdk/a11y/index.js',
3435
'@angular/cdk/accordion': 'dist/packages/cdk/accordion/index.js',

src/demo-app/tsconfig-aot.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
{
55
"extends": "./tsconfig-build",
66
"compilerOptions": {
7+
// Needed for Moment.js since it doesn't have a default export.
8+
"allowSyntheticDefaultImports": true,
79
"experimentalDecorators": true,
810
// TODO(paul): Remove once Angular has been upgraded and supports noUnusedParameters in AOT.
911
"noUnusedParameters": false,

src/demo-app/tsconfig-build.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
// since the demo-app will be served in the browser.
33
{
44
"compilerOptions": {
5+
// Needed for Moment.js since it doesn't have a default export.
6+
"allowSyntheticDefaultImports": true,
57
"declaration": false,
68
"emitDecoratorMetadata": true,
79
"experimentalDecorators": true,

src/demo-app/tsconfig.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,15 @@
22
{
33
"extends": "../../tsconfig.json",
44
"compilerOptions": {
5+
// Needed for Moment.js since it doesn't have a default export.
6+
"allowSyntheticDefaultImports": true,
57
"rootDir": "..",
68
"baseUrl": ".",
79
"paths": {
810
"@angular/cdk/*": ["../cdk/*"],
911
"@angular/material/*": ["../lib/*"],
10-
"@angular/material": ["../lib/public-api.ts"]
12+
"@angular/material": ["../lib/public-api.ts"],
13+
"@angular/material-moment-adapter": ["../material-moment-adapter/public-api.ts"]
1114
}
1215
},
1316
"include": ["./**/*.ts"]

src/e2e-app/system-config.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ System.config({
99
map: {
1010
'rxjs': 'node:rxjs',
1111
'main': 'main.js',
12+
'moment': 'node:moment/min/moment-with-locales.min.js',
1213

1314
// Angular specific mappings.
1415
'@angular/core': 'node:@angular/core/bundles/core.umd.js',
@@ -26,6 +27,7 @@ System.config({
2627
'node:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js',
2728

2829
'@angular/material': 'dist/bundles/material.umd.js',
30+
'@angular/material-moment-adapter': 'dist/bundles/material-moment-adapter.umd.js',
2931
'@angular/cdk': 'dist/bundles/cdk.umd.js',
3032
'@angular/cdk/a11y': 'dist/bundles/cdk-a11y.umd.js',
3133
'@angular/cdk/accordion': 'dist/bundles/cdk-accordion.umd.js',

src/e2e-app/tsconfig-build.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
"@angular/cdk/*": ["./cdk/*"],
2828
"@angular/material": ["./material"],
2929
"@angular/material/*": ["./material/*"],
30+
"@angular/material-moment-adapter": ["./material-moment-adapter"],
3031
"@angular/material-examples": ["./material-examples"]
3132
}
3233
},

src/e2e-app/tsconfig.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
"@angular/cdk/*": ["../cdk/*"],
99
"@angular/material/*": ["../lib/*"],
1010
"@angular/material": ["../lib/public-api.ts"],
11+
"@angular/material-moment-adapter": ["../material-moment-adapter/public-api.ts"],
1112
"@angular/material-examples": ["../material-examples/public-api.ts"]
1213
}
1314
},

src/lib/datepicker/datepicker-errors.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,6 @@
1010
export function createMissingDateImplError(provider: string) {
1111
return Error(
1212
`MatDatepicker: No provider found for ${provider}. You must import one of the following ` +
13-
`modules at your application root: MatNativeDateModule, or provide a custom implementation.`);
13+
`modules at your application root: MatNativeDateModule, MatMomentDateModule, or provide a ` +
14+
`custom implementation.`);
1415
}

src/lib/datepicker/datepicker.md

Lines changed: 125 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,10 @@
11
The datepicker allows users to enter a date either through text input, or by choosing a date from
2-
the calendar. It is made up of several components and directives that work together:
2+
the calendar. It is made up of several components and directives that work together.
33

44
<!-- example(datepicker-overview) -->
55

6-
### Current state
7-
Currently the datepicker is in the beginning stages and supports basic date selection functionality.
8-
There are many more features that will be added in future iterations, including:
9-
* Support for datetimes (e.g. May 2, 2017 at 12:30pm) and month + year only (e.g. May 2017)
10-
* Support for selecting and displaying date ranges
11-
* Support for custom time zones
12-
* Infinite scrolling through calendar months
13-
* Built in support for [Moment.js](https://momentjs.com/) dates
14-
156
### Connecting a datepicker to an input
7+
168
A datepicker is composed of a text input and a calendar pop-up, connected via the `matDatepicker`
179
property on the text input.
1810

@@ -41,19 +33,44 @@ can easily be used as a prefix or suffix on the material input:
4133
```
4234

4335
### Setting the calendar starting view
36+
4437
By default the calendar will open in month view, this can be changed by setting the `startView`
45-
property of `mat-datepicker` to `"year"`. In year view the user will see all months of the year and
38+
property of `<mat-datepicker>` to `year`. In year view the user will see all months of the year and
4639
then proceed to month view after choosing a month.
4740

4841
The month or year that the calendar opens to is determined by first checking if any date is
4942
currently selected, if so it will open to the month or year containing that date. Otherwise it will
5043
open to the month or year containing today's date. This behavior can be overridden by using the
51-
`startAt` property of `mat-datepicker`. In this case the calendar will open to the month or year
44+
`startAt` property of `<mat-datepicker>`. In this case the calendar will open to the month or year
5245
containing the `startAt` date.
5346

5447
<!-- example(datepicker-start-view) -->
5548

49+
### Setting the selected date
50+
51+
The type of values that the datepicker expects depends on the type of `DateAdapter` provided in your
52+
application. The `NativeDateAdapter`, for example, works directly with plain JavaScript `Date`
53+
objects. When using the `MomentDateAdapter`, however, the values will all be Moment.js instances.
54+
This use of the adapter pattern allows the datepicker component to work with any arbitrary date
55+
representation with a custom `DateAdapter`.
56+
See [_Choosing a date implementation_](#choosing-a-date-implementation-and-date-format-settings)
57+
for more information.
58+
59+
Depending on the `DateAdapter` being used, the datepicker may automatically deserialize certain date
60+
formats for you as well. For example, both the `NativeDateAdapter` and `MomentDateAdapter` allow
61+
[ISO 8601](https://tools.ietf.org/html/rfc3339) strings to be passed to the datepicker and
62+
automatically converted to the proper object type. This can be convenient when binding data directly
63+
from your backend to the datepicker. However, the datepicker will not accept date strings formatted
64+
in user format such as `"1/2/2017"` as this is ambiguous and will mean different things depending on
65+
the locale of the browser running the code.
66+
67+
As with other types of `<input>`, the datepicker works with `@angular/forms` directives such as
68+
`formGroup`, `formControl`, `ngModel`, etc.
69+
70+
<!-- example(datepicker-value) -->
71+
5672
### Date validation
73+
5774
There are three properties that add date validation to the datepicker input. The first two are the
5875
`min` and `max` properties. In addition to enforcing validation on the input, these properties will
5976
disable all dates on the calendar popup before or after the respective values and prevent the user
@@ -64,8 +81,8 @@ from advancing the calendar past the `month` or `year` (depending on current vie
6481

6582
The second way to add date validation is using the `matDatepickerFilter` property of the datepicker
6683
input. This property accepts a function of `<D> => boolean` (where `<D>` is the date type used by
67-
the datepicker, see section on
68-
[choosing a date implementation](#choosing-a-date-implementation-and-date-format-settings)).
84+
the datepicker, see
85+
[_Choosing a date implementation_](#choosing-a-date-implementation-and-date-format-settings)).
6986
A result of `true` indicates that the date is valid and a result of `false` indicates that it is
7087
not. Again this will also disable the dates on the calendar that are invalid. However, one important
7188
difference between using `matDatepickerFilter` vs using `min` or `max` is that filtering out all
@@ -84,38 +101,54 @@ Each validation property has a different error that can be checked:
84101
* A value that violates the `matDatepickerFilter` property will have a `matDatepickerFilter` error.
85102

86103
### Input and change events
87-
The input's native `input` and `change` events will only trigger due to user interaction with the
88-
input element; they will not fire when the user selects a date from the calendar popup. Because of
89-
this limitation, the datepicker input also has support for `dateInput` and `dateChange` events.
90-
These trigger when the user interacts with either the input or the popup.
91-
92-
```html
93-
<input [matDatepicker]="d" (dateInput)="onInput($event)" (dateChange)="onChange($event)">
94-
<mat-datepicker #d></mat-datepicker>
95-
```
104+
105+
The input's native `(input)` and `(change)` events will only trigger due to user interaction with
106+
the input element; they will not fire when the user selects a date from the calendar popup.
107+
Therefore, the datepicker input also has support for `(dateInput)` and `(dateChange)` events. These
108+
trigger when the user interacts with either the input or the popup.
109+
110+
The `(dateInput)` event will fire whenever the value changes due to the user typing or selecting a
111+
date from the calendar. The `(dateChange)` event will fire whenever the user finishes typing input
112+
(on `<input>` blur), or when the user chooses a date from the calendar.
113+
114+
<!-- example(datepicker-events) -->
115+
116+
### Disabling parts of the datepicker
117+
118+
As with any standard `<input>`, it is possible to disable the datepicker input by adding the
119+
`disabled` property. By default, the `<mat-datepicker>` and `<mat-datepicker-toggle>` will inherit
120+
their disabled state from the `<input>`, but this can be overridden by setting the `disabled`
121+
property on the datepicker or toggle elements. This can be useful if you want to disable text input
122+
but allow selection via the calendar or vice-versa.
123+
124+
<!-- example(datepicker-disabled) -->
96125

97126
### Touch UI mode
127+
98128
The datepicker normally opens as a popup under the input. However this is not ideal for touch
99129
devices that don't have as much screen real estate and need bigger click targets. For this reason
100-
`mat-datepicker` has a `touchUi` property that can be set to `true` in order to enable a more touch
101-
friendly UI where the calendar opens in a large dialog.
130+
`<mat-datepicker>` has a `touchUi` property that can be set to `true` in order to enable a more
131+
touch friendly UI where the calendar opens in a large dialog.
102132

103133
<!-- example(datepicker-touch) -->
104134

105135
### Manually opening and closing the calendar
136+
106137
The calendar popup can be programmatically controlled using the `open` and `close` methods on the
107-
`mat-datepicker`. It also has an `opened` property that reflects the status of the popup.
138+
`<mat-datepicker>`. It also has an `opened` property that reflects the status of the popup.
108139

109140
<!-- example(datepicker-api) -->
110141

111142
### Internationalization
112-
In order to support internationalization, the datepicker supports customization of the following
113-
three pieces via injection:
114-
1. The date implementation that the datepicker accepts.
115-
2. The display and parse formats used by the datepicker.
116-
3. The message strings used in the datepicker's UI.
143+
144+
Internationalization of the datepicker is configured via four aspects:
145+
1. The date locale.
146+
2. The date implementation that the datepicker accepts.
147+
3. The display and parse formats used by the datepicker.
148+
4. The message strings used in the datepicker's UI.
117149

118150
#### Setting the locale code
151+
119152
By default, the `MAT_DATE_LOCALE` injection token will use the existing `LOCALE_ID` locale code
120153
from `@angular/core`. If you want to override it, you can provide a new value for the
121154
`MAT_DATE_LOCALE` token:
@@ -131,28 +164,25 @@ export class MyApp {}
131164

132165
It's also possible to set the locale at runtime using the `setLocale` method of the `DateAdapter`.
133166

134-
```ts
135-
import { DateAdapter, NativeDateAdapter } from '@angular/material';
136-
137-
@Component({
138-
selector: 'foo',
139-
template: ''
140-
})
141-
export class FooComponent {
142-
constructor(dateAdapter: DateAdapter<NativeDateAdapter>) {
143-
dateAdapter.setLocale('de-DE');
144-
}
145-
}
146-
```
167+
<!-- example(datepicker-locale) -->
147168

148169
#### Choosing a date implementation and date format settings
170+
149171
The datepicker was built to be date implementation agnostic. This means that it can be made to work
150172
with a variety of different date implementations. However it also means that developers need to make
151173
sure to provide the appropriate pieces for the datepicker to work with their chosen implementation.
152-
The easiest way to ensure this is just to import one of the pre-made modules (currently
153-
`MatNativeDateModule` is the only implementation that ships with material, but there are plans to add
154-
a module for Moment.js support):
155-
* `MatNativeDateModule` - support for native JavaScript Date object
174+
The easiest way to ensure this is just to import one of the pre-made modules:
175+
176+
|Module |Date type|Supported locales |Dependencies |Import from |
177+
|---------------------|---------|-----------------------------------------------------------------------|----------------------------------|----------------------------------|
178+
|`MatNativeDateModule`|`Date` |en-US |None |`@angular/material` |
179+
|`MatMomentDateModule`|`Moment` |[See project](https://github.com/moment/moment/tree/develop/src/locale)|[Moment.js](https://momentjs.com/)|`@angular/material-moment-adapter`|
180+
181+
*Please note: `MatNativeDateModule` is based off of the functionality available in JavaScript's
182+
native `Date` object, and is thus not suitable for many locales. One of the biggest shortcomings of
183+
the native `Date` object is the inability to set the parse format. We highly recommend using the
184+
`MomentDateAdapter` or a custom `DateAdapter` that works with the formatting/parsing library of your
185+
choice.*
156186

157187
These modules include providers for `DateAdapter` and `MAT_DATE_FORMATS`
158188

@@ -175,16 +205,14 @@ export class MyComponent {
175205
}
176206
```
177207

178-
*Please note: `MatNativeDateModule` is based off of the functionality available in JavaScript's
179-
native `Date` object, and is thus not suitable for many locales. One of the biggest shortcomings of
180-
the native `Date` object is the inability to set the parse format. We highly recommend using a
181-
custom `DateAdapter` that works with the formatting/parsing library of your choice.*
208+
<!-- example(datepicker-moment) -->
182209

183-
#### Customizing the date implementation
184-
The datepicker does all of its interaction with date objects via the `DateAdapter`. Making the
185-
datepicker work with a different date implementation is as easy as extending `DateAdapter`, and
186-
using your subclass as the provider. You will also want to make sure that the `MAT_DATE_FORMATS`
187-
provided in your app are formats that can be understood by your date implementation.
210+
It is also possible to create your own `DateAdapter` that works with any date format your app
211+
requires. This is accomplished by subclassing `DateAdapter` and providing your subclass as the
212+
`DateAdapter` implementation. You will also want to make sure that the `MAT_DATE_FORMATS` provided
213+
in your app are formats that can be understood by your date implementation. See
214+
[_Customizing the parse and display formats_](#customizing-the-parse-and-display-formats)for more
215+
information about `MAT_DATE_FORMATS`. <!-- TODO(mmalerba): Add a guide about this -->
188216

189217
```ts
190218
@NgModule({
@@ -198,24 +226,30 @@ export class MyApp {}
198226
```
199227

200228
#### Customizing the parse and display formats
229+
201230
The `MAT_DATE_FORMATS` object is just a collection of formats that the datepicker uses when parsing
202231
and displaying dates. These formats are passed through to the `DateAdapter` so you will want to make
203232
sure that the format objects you're using are compatible with the `DateAdapter` used in your app.
204-
This example shows how to use the native `Date` implementation from material, but with custom
205-
formats.
233+
234+
If you want use one of the `DateAdapters` that ships with Angular Material, but use your own
235+
`MAT_DATE_FORMATS`, you can import the `NativeDateModule` or `MomentDateModule`. These modules are
236+
identical to the "Mat"-prefixed versions (`MatNativeDateModule` and `MatMomentDateModule`) except
237+
they do not include the default formats. For example:
206238

207239
```ts
208240
@NgModule({
209-
imports: [MatDatepickerModule],
241+
imports: [MatDatepickerModule, NativeDateModule],
210242
providers: [
211-
{provide: DateAdapter, useClass: NativeDateAdapter},
212243
{provide: MAT_DATE_FORMATS, useValue: MY_NATIVE_DATE_FORMATS},
213244
],
214245
})
215246
export class MyApp {}
216247
```
217248

249+
<!-- example(datepicker-formats) -->
250+
218251
#### Localizing labels and messages
252+
219253
The various text strings used by the datepicker are provided through `MatDatepickerIntl`.
220254
Localization of these messages can be done by providing a subclass with translated values in your
221255
application root module.
@@ -229,7 +263,9 @@ application root module.
229263
})
230264
export class MyApp {}
231265
```
266+
232267
### Accessibility
268+
233269
The `MatDatepickerInput` directive adds `aria-haspopup` attribute to the native input element, and it
234270
triggers a calendar dialog with `role="dialog"`.
235271

@@ -238,6 +274,7 @@ should have a placeholder or be given a meaningful label via `aria-label`, `aria
238274
`MatDatepickerIntl`.
239275

240276
#### Keyboard shortcuts
277+
241278
The keyboard shortcuts to handle datepicker are:
242279

243280
| Shortcut | Action |
@@ -278,3 +315,30 @@ In year view:
278315
| `PAGE_DOWN` | Go to next year |
279316
| `ALT` + `PAGE_DOWN` | Go to next 10 years |
280317
| `ENTER` | Select current month |
318+
319+
### Troubleshooting
320+
321+
#### Error: MatDatepicker: No provider found for DateAdapter/MAT_DATE_FORMATS
322+
323+
This error is thrown if you have not provided all of the injectables the datepicker needs to work.
324+
The easiest way to resolve this is to import the `MatNativeDateModule` or `MatMomentDateModule` in
325+
your application's root module. See
326+
[_Choosing a date implementation_](#choosing-a-date-implementation-and-date-format-settings)) for
327+
more information.
328+
329+
#### Error: A MatDatepicker can only be associated with a single input
330+
331+
This error is thrown if more than one `<input>` tries to claim ownership over the same
332+
`<mat-datepicker>` (via the `matDatepicker` attribute on the input). A datepicker can only be
333+
associated with a single input.
334+
335+
#### Error: Attempted to open an MatDatepicker with no associated input.
336+
337+
This error occurs if your `<mat-datepicker>` is not associated with any `<input>`. To associate an
338+
input with your datepicker, create a template reference for the datepicker and assign it to the
339+
`matDatepicker` attribute on the input:
340+
341+
```html
342+
<input [matDatepicker]="picker">
343+
<mat-datepicker #picker></mat-datepicker>
344+
```

src/lib/datepicker/datepicker.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,7 @@ export class MatDatepicker<D> implements OnDestroy {
250250
*/
251251
_registerInput(input: MatDatepickerInput<D>): void {
252252
if (this._datepickerInput) {
253-
throw Error('An MatDatepicker can only be associated with a single input.');
253+
throw Error('A MatDatepicker can only be associated with a single input.');
254254
}
255255
this._datepickerInput = input;
256256
this._inputSubscription =

0 commit comments

Comments
 (0)