Skip to content

Commit

Permalink
feat(DlDateAdapterString): add support for string date in model
Browse files Browse the repository at this point in the history
Also updated docs and simplified release configuration in package.json

Fixes #412
  • Loading branch information
dalelotts committed Apr 8, 2018
1 parent fa18b9e commit f645ffb
Show file tree
Hide file tree
Showing 6 changed files with 276 additions and 3 deletions.
72 changes: 72 additions & 0 deletions src/lib/dl-date-time-picker/dl-date-adapter-string.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import {DlDateAdapter} from './dl-date-adapter';
import * as _moment from 'moment';
import {Inject, InjectionToken} from '@angular/core';

/**
* Work around for moment namespace conflict when used with webpack and rollup.
* See https://github.com/dherges/ng-packagr/issues/163
*
* Depending on whether rollup is used, moment needs to be imported differently.
* Since Moment.js doesn't have a default export, we normally need to import using
* the `* as`syntax.
*
* rollup creates a synthetic default module and we thus need to import it using
* the `default as` syntax.
*
* @internal
*
**/
const moment = _moment;


/**
* InjectionToken for string dates that can be used to override default output format.
**/
export const DL_STRING_DATE_OUTPUT_FORMAT = new InjectionToken<string>('DL_STRING_DATE_OUTPUT_FORMAT');

/**
* InjectionToken for string dates that can be used to override default input formats.
**/
export const DL_STRING_DATE_INPUT_FORMATS = new InjectionToken<string[]>('DL_STRING_DATE_INPUT_FORMATS');

/**
* Adapts `string` to be usable as a date by date/time components that work with dates.
**/
export class DlDateAdapterString extends DlDateAdapter<string> {

private readonly modelFormat: string;
private readonly inputFormats: string[];

constructor(@Inject(DL_STRING_DATE_INPUT_FORMATS) inputFormats: string[],
@Inject(DL_STRING_DATE_OUTPUT_FORMAT) modelFormat: string) {
super();
this.inputFormats = inputFormats;
this.modelFormat = modelFormat;
}

/**
* Returns the specified number.
* @param milliseconds
* a moment time time.
* @returns
* the specified moment in time.
*/
fromMilliseconds(milliseconds: number): string {
return moment(milliseconds).format(this.modelFormat);
}

/**
* Returns the specified number.
* @param value
* a moment time time or `null`
* @returns
* the milliseconds for the specified value or `null`
* `null` is returned when value is not a valid input date string
*/
toMilliseconds(value: string | null): number | null {
if (value !== undefined && value !== null) {
const newMoment = moment(value, this.inputFormats, true);
return newMoment.isValid() ? newMoment.valueOf() : undefined;
}
}
}
31 changes: 29 additions & 2 deletions src/lib/dl-date-time-picker/dl-date-time-picker.component.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,44 @@ The following keyboard shortcuts are supported in in all views:
| `PAGE_DOWN` | Go to the same cell in the next time period |
| `ENTER` or `SPACE` | Select current cell |

## Date Adapter
## Supported date types

Import the module corresponding to the desired data type of the date in the model.
* Native JavaScript Date: import 'DlDateTimePickerDateModule'
* Moment Date: import 'DlDateTimePickerMomentModule'
* Milliseconds (local): import 'DlDateTimePickerNumberModule'
* String (local): import 'DlDateTimePickerStringModule'

A `DateAdapter` is used to adapt the data type in the model to the `number` data type
used internally by the date/time picker.

If you need a different data type than what is currently supported, you can extend
`DlDateAdapter<D>` to provide the data type you need then override the `DlDateAdapter`
provider in `app.module.ts` to use your new class.

`providers: [{provide: DlDateAdapter, useClass: MyCustomDateAdapter}],`

### String Date Adapter Formats
The input and output formats for dates are injected into the `DlDateAdapterString` class
using the `DL_STRING_DATE_INPUT_FORMATS` and `DL_STRING_DATE_OUTPUT_FORMAT` tokens.

`DL_STRING_DATE_OUTPUT_FORMAT` defaults to `moment`'s `lll` long date format.

`DL_STRING_DATE_INPUT_FORMATS` defaults to the following:
```typescript
[
moment.localeData().longDateFormat('lll'),
'YYYY-MM-DDTHH:mm',
'YYYY-MM-DDTHH:mm:ss',
'YYYY-MM-DDTHH:mm:ss.SSS',
'YYYY-MM-DD',
'YYYY-MM-DDTHH:mm:ss.SSS[Z]' // ISO_8601
]
```

If you want different formats, override the injection tokens in `app.module.ts`
i.e `{provide: DL_STRING_DATE_OUTPUT_FORMAT, useValue: '<what ever format you want goes here>'}`

## Model Provider

`ModelProvider`s are used to create the `Model` for each of the `views` in the date/time picker.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -455,7 +455,7 @@ export class DlDateTimePickerComponent<D> implements OnChanges, OnInit, ControlV
**/
private getStartDate() {
if (hasValue(this._value)) {
return this._value;
return this._dateAdapter.toMilliseconds(this._value);
}
if (hasValue(this.startDate)) {
return this.startDate;
Expand Down
44 changes: 44 additions & 0 deletions src/lib/dl-date-time-picker/dl-date-time-picker.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,25 @@ import {DlMonthModelProvider} from './dl-model-provider-month';
import {DlDayModelProvider} from './dl-model-provider-day';
import {DlHourModelProvider} from './dl-model-provider-hour';
import {DlMinuteModelProvider} from './dl-model-provider-minute';
import {DL_STRING_DATE_INPUT_FORMATS, DL_STRING_DATE_OUTPUT_FORMAT, DlDateAdapterString} from './dl-date-adapter-string';

import * as _moment from 'moment';

/**
* Work around for moment namespace conflict when used with webpack and rollup.
* See https://github.com/dherges/ng-packagr/issues/163
*
* Depending on whether rollup is used, moment needs to be imported differently.
* Since Moment.js doesn't have a default export, we normally need to import using
* the `* as`syntax.
*
* rollup creates a synthetic default module and we thus need to import it using
* the `default as` syntax.
*
* @internal
*
**/
const moment = _moment;

/**
* Import this module to supply your own `DateAdapter` provider.
Expand Down Expand Up @@ -72,3 +91,28 @@ export class DlDateTimePickerDateModule {
})
export class DlDateTimePickerMomentModule {
}

/**
* Import this module to store a `string` in the model.
*/
@NgModule({
imports: [DlDateTimePickerModule],
exports: [DlDateTimePickerComponent],
providers: [
{
provide: DL_STRING_DATE_INPUT_FORMATS, useValue: [
moment.localeData().longDateFormat('lll'),
'YYYY-MM-DDTHH:mm',
'YYYY-MM-DDTHH:mm:ss',
'YYYY-MM-DDTHH:mm:ss.SSS',
'YYYY-MM-DD',
moment.ISO_8601
]
},
{provide: DL_STRING_DATE_OUTPUT_FORMAT, useValue: moment.localeData().longDateFormat('lll')},
{provide: DlDateAdapter, useClass: DlDateAdapterString}
],
})
export class DlDateTimePickerStringModule {
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import {DlDateAdapterString} from '../../dl-date-adapter-string';
import {DlDateAdapter} from '../../dl-date-adapter';

import * as _moment from 'moment';
import {Moment} from 'moment';

/**
* Work around for moment namespace conflict when used with webpack and rollup.
* See https://github.com/dherges/ng-packagr/issues/163
*
* Depending on whether rollup is used, moment needs to be imported differently.
* Since Moment.js doesn't have a default export, we normally need to import using
* the `* as`syntax.
*
* rollup creates a synthetic default module and we thus need to import it using
* the `default as` syntax.
*
* @internal
*
**/
const moment = _moment;

/**
* @license
* Copyright 2013-present Dale Lotts All Rights Reserved.
* http://www.dalelotts.com
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://github.com/dalelotts/angular-bootstrap-datetimepicker/blob/master/LICENSE
*/



describe('DlDateAdapterString', () => {

describe('default configuration', () => {
let dateAdapter: DlDateAdapterString;
let testMoment: Moment;
beforeEach(() => {
testMoment = moment(1523077200000);
dateAdapter = new DlDateAdapterString([
moment.localeData().longDateFormat('lll'),
'YYYY-MM-DDTHH:mm',
'YYYY-MM-DDTHH:mm:ss',
'YYYY-MM-DDTHH:mm:ss.SSS',
'YYYY-MM-DD',
'YYYY-MM-DDTHH:mm:ss.SSS[Z]' // ISO_8601
],
moment.localeData().longDateFormat('lll')
);
});

describe('fromMilliseconds', () => {
it('should return "lll" moment format', () => {
expect(dateAdapter.fromMilliseconds(1523077200000)).toEqual(testMoment.format('lll'));
});
});

describe('toMilliseconds', () => {
it('should accept "lll" moment format', () => {
expect(dateAdapter.toMilliseconds(testMoment.format('lll'))).toEqual(1523077200000);
});
it('should return undefined for invalid date value', () => {
expect(dateAdapter.toMilliseconds('Aor 7, 2018 12:00 AM')).toBeUndefined();
});
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/**
* @license
* Copyright 2013-present Dale Lotts All Rights Reserved.
* http://www.dalelotts.com
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://github.com/dalelotts/angular-bootstrap-datetimepicker/blob/master/LICENSE
*/

import {DlDateTimePickerComponent} from '../../dl-date-time-picker.component';
import {Component, DebugElement, ViewChild} from '@angular/core';
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
import {By} from '@angular/platform-browser';
import {DlDateTimePickerStringModule} from '../../index';


@Component({
template: '<dl-date-time-picker minView="day"></dl-date-time-picker>'
})
class ModelTypeComponent {
@ViewChild(DlDateTimePickerComponent) picker: DlDateTimePickerComponent<string>;
}

describe('DlDateTimePickerComponent modelType', () => {

beforeEach(async(() => {
return TestBed.configureTestingModule({
imports: [
DlDateTimePickerStringModule
],
declarations: [
ModelTypeComponent,
]
})
.compileComponents();
}));

describe('string formatted Date', () => {
let component: ModelTypeComponent;
let fixture: ComponentFixture<ModelTypeComponent>;
let debugElement: DebugElement;
let nativeElement: any;

beforeEach(async(() => {
fixture = TestBed.createComponent(ModelTypeComponent);
fixture.detectChanges();
fixture.whenStable().then(() => {
fixture.detectChanges();
component = fixture.componentInstance;
debugElement = fixture.debugElement;
nativeElement = debugElement.nativeElement;
});
}));

it('should be string type', () => {
const nowElement = fixture.debugElement.query(By.css('.dl-abdtp-now'));
nowElement.nativeElement.click();

expect(component.picker.value).toEqual(jasmine.any(String));
});
});
});

0 comments on commit f645ffb

Please sign in to comment.