feat(moment-date-adapter): implement strict mode (#16462)

* feat(moment-date-adapter): implement strict mode

Moment supports a strict flag which solves a lot of parsing errors.

Examples:

| Input           | Format       | Forgiving  | Strict       |
|-----------------|--------------|------------|--------------|
| 1/2/2017        | M/D/YYYY     | 2017-01-02 | 2017-01-02   |
| 1/2/2017        | MM/DD/YYYY   | 2017-01-02 | Invalid date |
| Januari 1, 2017 | MMMM D, YYYY | 2017-01-01 | Invalid date |
| Jan 1, 2017     | MMMM D, YYYY | 2017-01-01 | Invalid date |
| 2017-1-1        | MMMM D, YYYY | 2017-01-20 | Invalid date |

* docs(datepicker): remove spaces in useUtc example

* refactor(moment-date-adapter): change toEqual -> ToBe in strict mode boolean tests

* refactor(moment-date-adapter): destructure options

useUtc has been made optional to prevent the necessity for default
values.

* docs(datepicker): remove spaces around strict example braces

* docs(datepicker): add links to Moment's strict/forgiving mode
This commit is contained in:
Matthias Kunnen
2019-08-06 21:46:50 +02:00
committed by mmalerba
parent 8658c30481
commit dd4295608a
3 changed files with 70 additions and 5 deletions
@@ -463,4 +463,39 @@ describe('MomentDateAdapter with MAT_MOMENT_DATE_ADAPTER_OPTIONS override', () =
});
});
describe('strict mode', () => {
beforeEach(async(() => {
TestBed.resetTestingModule();
TestBed.configureTestingModule({
imports: [MomentDateModule],
providers: [
{
provide: MAT_MOMENT_DATE_ADAPTER_OPTIONS,
useValue: {
strict: true,
},
},
]
}).compileComponents();
}));
beforeEach(inject([DateAdapter], (d: MomentDateAdapter) => {
adapter = d;
}));
it('should detect valid strings according to given format', () => {
expect(adapter.parse('1/2/2017', 'D/M/YYYY')!.format('l'))
.toEqual(moment([2017, FEB, 1]).format('l'));
expect(adapter.parse('February 1, 2017', 'MMMM D, YYYY')!.format('l'))
.toEqual(moment([2017, FEB, 1]).format('l'));
});
it('should detect invalid strings according to given format', () => {
expect(adapter.parse('2017-01-01', 'MM/DD/YYYY')!.isValid()).toBe(false);
expect(adapter.parse('1/2/2017', 'MM/DD/YYYY')!.isValid()).toBe(false);
expect(adapter.parse('Jan 5, 2017', 'MMMM D, YYYY')!.isValid()).toBe(false);
});
});
});
@@ -15,18 +15,25 @@ import {DateAdapter, MAT_DATE_LOCALE} from '@angular/material/core';
// TODO(mmalerba): See if we can clean this up at some point.
import * as _moment from 'moment';
// tslint:disable-next-line:no-duplicate-imports
import {default as _rollupMoment, Moment} from 'moment';
import {default as _rollupMoment, Moment, MomentFormatSpecification, MomentInput} from 'moment';
const moment = _rollupMoment || _moment;
/** Configurable options for {@see MomentDateAdapter}. */
export interface MatMomentDateAdapterOptions {
/**
* When enabled, the dates have to match the format exactly.
* See https://momentjs.com/guides/#/parsing/strict-mode/.
*/
strict?: boolean;
/**
* Turns the use of utc dates on or off.
* Changing this will change how Angular Material components like DatePicker output dates.
* {@default false}
*/
useUtc: boolean;
useUtc?: boolean;
}
/** InjectionToken for moment date adapter to configure options. */
@@ -241,7 +248,15 @@ export class MomentDateAdapter extends DateAdapter<Moment> {
}
/** Creates a Moment instance while respecting the current UTC settings. */
private _createMoment(...args: any[]): Moment {
return (this._options && this._options.useUtc) ? moment.utc(...args) : moment(...args);
private _createMoment(
date: MomentInput,
format?: MomentFormatSpecification,
locale?: string,
): Moment {
const {strict, useUtc}: MatMomentDateAdapterOptions = this._options || {};
return useUtc
? moment.utc(date, format, locale, strict)
: moment(date, format, locale, strict);
}
}
+16 -1
View File
@@ -252,7 +252,22 @@ By default the `MomentDateAdapter` will creates dates in your time zone specific
@NgModule({
imports: [MatDatepickerModule, MatMomentDateModule],
providers: [
{ provide: MAT_MOMENT_DATE_ADAPTER_OPTIONS, useValue: { useUtc: true } }
{provide: MAT_MOMENT_DATE_ADAPTER_OPTIONS, useValue: {useUtc: true}}
]
})
```
By default the `MomentDateAdapter` will parse dates in a
[forgiving way](https://momentjs.com/guides/#/parsing/forgiving-mode/). This may result in dates
being parsed incorrectly. You can change the default behaviour to
[parse dates strictly](https://momentjs.com/guides/#/parsing/strict-mode/) by providing
the `MAT_MOMENT_DATE_ADAPTER_OPTIONS` and setting it to `strict: true`.
```ts
@NgModule({
imports: [MatDatepickerModule, MatMomentDateModule],
providers: [
{provide: MAT_MOMENT_DATE_ADAPTER_OPTIONS, useValue: {strict: true}}
]
})
```