Skip to content

docs(datepicker): update docs and add examples #7837

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Oct 21, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/demo-app/system-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ System.config({

// TODO(devversion): replace once the index.ts file for the Material package has been added.
'@angular/material': 'dist/packages/material/public-api.js',
'@angular/material-moment-adapter': 'dist/packages/material-moment-adapter/public-api.js',
'@angular/cdk': 'dist/packages/cdk/index.js',
'@angular/cdk/a11y': 'dist/packages/cdk/a11y/index.js',
'@angular/cdk/accordion': 'dist/packages/cdk/accordion/index.js',
Expand Down
2 changes: 2 additions & 0 deletions src/demo-app/tsconfig-aot.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
{
"extends": "./tsconfig-build",
"compilerOptions": {
// Needed for Moment.js since it doesn't have a default export.
"allowSyntheticDefaultImports": true,
"experimentalDecorators": true,
// TODO(paul): Remove once Angular has been upgraded and supports noUnusedParameters in AOT.
"noUnusedParameters": false,
Expand Down
2 changes: 2 additions & 0 deletions src/demo-app/tsconfig-build.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
// since the demo-app will be served in the browser.
{
"compilerOptions": {
// Needed for Moment.js since it doesn't have a default export.
"allowSyntheticDefaultImports": true,
"declaration": false,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
Expand Down
5 changes: 4 additions & 1 deletion src/demo-app/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
// Needed for Moment.js since it doesn't have a default export.
"allowSyntheticDefaultImports": true,
"rootDir": "..",
"baseUrl": ".",
"paths": {
"@angular/cdk/*": ["../cdk/*"],
"@angular/material/*": ["../lib/*"],
"@angular/material": ["../lib/public-api.ts"]
"@angular/material": ["../lib/public-api.ts"],
"@angular/material-moment-adapter": ["../material-moment-adapter/public-api.ts"]
}
},
"include": ["./**/*.ts"]
Expand Down
2 changes: 2 additions & 0 deletions src/e2e-app/system-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ System.config({
map: {
'rxjs': 'node:rxjs',
'main': 'main.js',
'moment': 'node:moment/min/moment-with-locales.min.js',

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

'@angular/material': 'dist/bundles/material.umd.js',
'@angular/material-moment-adapter': 'dist/bundles/material-moment-adapter.umd.js',
'@angular/cdk': 'dist/bundles/cdk.umd.js',
'@angular/cdk/a11y': 'dist/bundles/cdk-a11y.umd.js',
'@angular/cdk/accordion': 'dist/bundles/cdk-accordion.umd.js',
Expand Down
1 change: 1 addition & 0 deletions src/e2e-app/tsconfig-build.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"@angular/cdk/*": ["./cdk/*"],
"@angular/material": ["./material"],
"@angular/material/*": ["./material/*"],
"@angular/material-moment-adapter": ["./material-moment-adapter"],
"@angular/material-examples": ["./material-examples"]
}
},
Expand Down
1 change: 1 addition & 0 deletions src/e2e-app/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"@angular/cdk/*": ["../cdk/*"],
"@angular/material/*": ["../lib/*"],
"@angular/material": ["../lib/public-api.ts"],
"@angular/material-moment-adapter": ["../material-moment-adapter/public-api.ts"],
"@angular/material-examples": ["../material-examples/public-api.ts"]
}
},
Expand Down
3 changes: 2 additions & 1 deletion src/lib/datepicker/datepicker-errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@
export function createMissingDateImplError(provider: string) {
return Error(
`MatDatepicker: No provider found for ${provider}. You must import one of the following ` +
`modules at your application root: MatNativeDateModule, or provide a custom implementation.`);
`modules at your application root: MatNativeDateModule, MatMomentDateModule, or provide a ` +
`custom implementation.`);
}
186 changes: 125 additions & 61 deletions src/lib/datepicker/datepicker.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,10 @@
The datepicker allows users to enter a date either through text input, or by choosing a date from
the calendar. It is made up of several components and directives that work together:
the calendar. It is made up of several components and directives that work together.

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

### Current state
Currently the datepicker is in the beginning stages and supports basic date selection functionality.
There are many more features that will be added in future iterations, including:
* Support for datetimes (e.g. May 2, 2017 at 12:30pm) and month + year only (e.g. May 2017)
* Support for selecting and displaying date ranges
* Support for custom time zones
* Infinite scrolling through calendar months
* Built in support for [Moment.js](https://momentjs.com/) dates

### Connecting a datepicker to an input

A datepicker is composed of a text input and a calendar pop-up, connected via the `matDatepicker`
property on the text input.

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

### Setting the calendar starting view

By default the calendar will open in month view, this can be changed by setting the `startView`
property of `mat-datepicker` to `"year"`. In year view the user will see all months of the year and
property of `<mat-datepicker>` to `year`. In year view the user will see all months of the year and
then proceed to month view after choosing a month.

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

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

### Setting the selected date

The type of values that the datepicker expects depends on the type of `DateAdapter` provided in your
application. The `NativeDateAdapter`, for example, works directly with plain JavaScript `Date`
objects. When using the `MomentDateAdapter`, however, the values will all be Moment.js instances.
This use of the adapter pattern allows the datepicker component to work with any arbitrary date
representation with a custom `DateAdapter`.
See [_Choosing a date implementation_](#choosing-a-date-implementation-and-date-format-settings)
for more information.

Depending on the `DateAdapter` being used, the datepicker may automatically deserialize certain date
formats for you as well. For example, both the `NativeDateAdapter` and `MomentDateAdapter` allow
[ISO 8601](https://tools.ietf.org/html/rfc3339) strings to be passed to the datepicker and
automatically converted to the proper object type. This can be convenient when binding data directly
from your backend to the datepicker. However, the datepicker will not accept date strings formatted
in user format such as `"1/2/2017"` as this is ambiguous and will mean different things depending on
the locale of the browser running the code.

As with other types of `<input>`, the datepicker works with `@angular/forms` directives such as
`formGroup`, `formControl`, `ngModel`, etc.

<!-- example(datepicker-value) -->

### Date validation

There are three properties that add date validation to the datepicker input. The first two are the
`min` and `max` properties. In addition to enforcing validation on the input, these properties will
disable all dates on the calendar popup before or after the respective values and prevent the user
Expand All @@ -64,8 +81,8 @@ from advancing the calendar past the `month` or `year` (depending on current vie

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

### Input and change events
The input's native `input` and `change` events will only trigger due to user interaction with the
input element; they will not fire when the user selects a date from the calendar popup. Because of
this limitation, the datepicker input also has support for `dateInput` and `dateChange` events.
These trigger when the user interacts with either the input or the popup.

```html
<input [matDatepicker]="d" (dateInput)="onInput($event)" (dateChange)="onChange($event)">
<mat-datepicker #d></mat-datepicker>
```

The input's native `(input)` and `(change)` events will only trigger due to user interaction with
the input element; they will not fire when the user selects a date from the calendar popup.
Therefore, the datepicker input also has support for `(dateInput)` and `(dateChange)` events. These
trigger when the user interacts with either the input or the popup.

The `(dateInput)` event will fire whenever the value changes due to the user typing or selecting a
date from the calendar. The `(dateChange)` event will fire whenever the user finishes typing input
(on `<input>` blur), or when the user chooses a date from the calendar.

<!-- example(datepicker-events) -->

### Disabling parts of the datepicker

As with any standard `<input>`, it is possible to disable the datepicker input by adding the
`disabled` property. By default, the `<mat-datepicker>` and `<mat-datepicker-toggle>` will inherit
their disabled state from the `<input>`, but this can be overridden by setting the `disabled`
property on the datepicker or toggle elements. This can be useful if you want to disable text input
but allow selection via the calendar or vice-versa.

<!-- example(datepicker-disabled) -->

### Touch UI mode

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

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

### Manually opening and closing the calendar

The calendar popup can be programmatically controlled using the `open` and `close` methods on the
`mat-datepicker`. It also has an `opened` property that reflects the status of the popup.
`<mat-datepicker>`. It also has an `opened` property that reflects the status of the popup.

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

### Internationalization
In order to support internationalization, the datepicker supports customization of the following
three pieces via injection:
1. The date implementation that the datepicker accepts.
2. The display and parse formats used by the datepicker.
3. The message strings used in the datepicker's UI.

Internationalization of the datepicker is configured via four aspects:
1. The date locale.
2. The date implementation that the datepicker accepts.
3. The display and parse formats used by the datepicker.
4. The message strings used in the datepicker's UI.

#### Setting the locale code

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

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

```ts
import { DateAdapter, NativeDateAdapter } from '@angular/material';

@Component({
selector: 'foo',
template: ''
})
export class FooComponent {
constructor(dateAdapter: DateAdapter<NativeDateAdapter>) {
dateAdapter.setLocale('de-DE');
}
}
```
<!-- example(datepicker-locale) -->

#### Choosing a date implementation and date format settings

The datepicker was built to be date implementation agnostic. This means that it can be made to work
with a variety of different date implementations. However it also means that developers need to make
sure to provide the appropriate pieces for the datepicker to work with their chosen implementation.
The easiest way to ensure this is just to import one of the pre-made modules (currently
`MatNativeDateModule` is the only implementation that ships with material, but there are plans to add
a module for Moment.js support):
* `MatNativeDateModule` - support for native JavaScript Date object
The easiest way to ensure this is just to import one of the pre-made modules:

|Module |Date type|Supported locales |Dependencies |Import from |
|---------------------|---------|-----------------------------------------------------------------------|----------------------------------|----------------------------------|
|`MatNativeDateModule`|`Date` |en-US |None |`@angular/material` |
|`MatMomentDateModule`|`Moment` |[See project](https://github.com/moment/moment/tree/develop/src/locale)|[Moment.js](https://momentjs.com/)|`@angular/material-moment-adapter`|

*Please note: `MatNativeDateModule` is based off of the functionality available in JavaScript's
native `Date` object, and is thus not suitable for many locales. One of the biggest shortcomings of
the native `Date` object is the inability to set the parse format. We highly recommend using the
`MomentDateAdapter` or a custom `DateAdapter` that works with the formatting/parsing library of your
choice.*

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

Expand All @@ -175,16 +205,14 @@ export class MyComponent {
}
```

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

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

```ts
@NgModule({
Expand All @@ -198,24 +226,30 @@ export class MyApp {}
```

#### Customizing the parse and display formats

The `MAT_DATE_FORMATS` object is just a collection of formats that the datepicker uses when parsing
and displaying dates. These formats are passed through to the `DateAdapter` so you will want to make
sure that the format objects you're using are compatible with the `DateAdapter` used in your app.
This example shows how to use the native `Date` implementation from material, but with custom
formats.

If you want use one of the `DateAdapters` that ships with Angular Material, but use your own
`MAT_DATE_FORMATS`, you can import the `NativeDateModule` or `MomentDateModule`. These modules are
identical to the "Mat"-prefixed versions (`MatNativeDateModule` and `MatMomentDateModule`) except
they do not include the default formats. For example:

```ts
@NgModule({
imports: [MatDatepickerModule],
imports: [MatDatepickerModule, NativeDateModule],
providers: [
{provide: DateAdapter, useClass: NativeDateAdapter},
{provide: MAT_DATE_FORMATS, useValue: MY_NATIVE_DATE_FORMATS},
],
})
export class MyApp {}
```

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

#### Localizing labels and messages

The various text strings used by the datepicker are provided through `MatDatepickerIntl`.
Localization of these messages can be done by providing a subclass with translated values in your
application root module.
Expand All @@ -229,7 +263,9 @@ application root module.
})
export class MyApp {}
```

### Accessibility

The `MatDatepickerInput` directive adds `aria-haspopup` attribute to the native input element, and it
triggers a calendar dialog with `role="dialog"`.

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

#### Keyboard shortcuts

The keyboard shortcuts to handle datepicker are:

| Shortcut | Action |
Expand Down Expand Up @@ -278,3 +315,30 @@ In year view:
| `PAGE_DOWN` | Go to next year |
| `ALT` + `PAGE_DOWN` | Go to next 10 years |
| `ENTER` | Select current month |

### Troubleshooting

#### Error: MatDatepicker: No provider found for DateAdapter/MAT_DATE_FORMATS

This error is thrown if you have not provided all of the injectables the datepicker needs to work.
The easiest way to resolve this is to import the `MatNativeDateModule` or `MatMomentDateModule` in
your application's root module. See
[_Choosing a date implementation_](#choosing-a-date-implementation-and-date-format-settings)) for
more information.

#### Error: A MatDatepicker can only be associated with a single input

This error is thrown if more than one `<input>` tries to claim ownership over the same
`<mat-datepicker>` (via the `matDatepicker` attribute on the input). A datepicker can only be
associated with a single input.

#### Error: Attempted to open an MatDatepicker with no associated input.

This error occurs if your `<mat-datepicker>` is not associated with any `<input>`. To associate an
input with your datepicker, create a template reference for the datepicker and assign it to the
`matDatepicker` attribute on the input:

```html
<input [matDatepicker]="picker">
<mat-datepicker #picker></mat-datepicker>
```
2 changes: 1 addition & 1 deletion src/lib/datepicker/datepicker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ export class MatDatepicker<D> implements OnDestroy {
*/
_registerInput(input: MatDatepickerInput<D>): void {
if (this._datepickerInput) {
throw Error('An MatDatepicker can only be associated with a single input.');
throw Error('A MatDatepicker can only be associated with a single input.');
}
this._datepickerInput = input;
this._inputSubscription =
Expand Down
Loading