@eqproject/eqp-datetimerangepicker
v21.0.1
Published
Datetimerange picker component - Angular Material based
Downloads
491
Readme
Table of contents
Required
- [x] Angular Material installed and imported
- [x] @angular-material-components/datetime-picker (^15.0.0)
- [x] @angular-material-components/moment-adapter (v9.0.0)
- [x] @angular/material-moment-adapter (15.2.9)
- [x] Moment.js
- [x] @ngx-translate/core
- [x] @ng-bootstrap/ng-bootstrap
Getting started
This package is based on Angular Material and Moment.js and allows you to create 4 kinds of date/time picker: date only, time only, both (datetime), and a date range.
Notes
By default the component returns the selected date converted to UTC timezone. All dates are normalized internally via Moment.js before being emitted. See Notes on behavior for the full details.
Step 1: Install eqp-datetimerangepicker:
NPM
npm i --save @eqproject/eqp-datetimerangepickerIf needed dependencies are not installed run these commands:
npm i @angular-material-components/[email protected]
npm i --save @angular-material-components/moment-adapter
npm i moment --save
npm i @ngx-translate/core --save
npm i @angular/material-moment-adapter --save
npm i @ng-bootstrap/ng-bootstrap --save
ng add @angular/materialStep 2:
Import EqpDatetimerangepickerModule, NgxMatDatetimePickerModule and NgxMatTimepickerModule :
import { EqpDatetimerangepickerModule } from "@eqproject/eqp-datetimerangepicker";
import { NgxMatDatetimePickerModule, NgxMatTimepickerModule } from "@angular-material-components/datetime-picker";
@NgModule({
declarations: [AppComponent],
imports: [EqpDatetimerangepickerModule, NgxMatDatetimePickerModule, NgxMatTimepickerModule],
bootstrap: [AppComponent]
})
export class AppModule {}Step 4: Invoke the loadTranslateService method
This step is mandatory as it allows eqp-datetimerangepicker to load an external TranslateService (which must reside in the project) and which will be used to change the language of the default presets in the DATE_RANGE picker.
import { EqpDatetimerangepickerModule, EqpDatetimerangepickerService } from "@eqproject/eqp-datetimerangepicker";
import { TranslateLoader, TranslateModule, TranslateService } from "@ngx-translate/core";
import { TranslateHttpLoader } from "@ngx-translate/http-loader";
import { HttpClient } from "@angular/common/http";
import { LOCALE_ID, NgModule } from "@angular/core";
import { registerLocaleData } from "@angular/common";
import localeIt from "@angular/common/locales/it";
import { MAT_DATE_LOCALE } from "@angular/material/core";
registerLocaleData(localeIt, "it-IT");
@NgModule({
declarations: [AppComponent],
imports: [
EqpDatetimerangepickerModule,
NgxMatDatetimePickerModule,
NgxMatTimepickerModule,
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useFactory: HttpLoaderFactory,
deps: [HttpClient]
},
isolate: false
})
],
providers: [
{ provide: MAT_DATE_LOCALE, useValue: "it-IT" },
{ provide: LOCALE_ID, useValue: "it-IT" }
],
bootstrap: [AppComponent]
})
export class AppModule {
constructor(
private eqpDateTimeRangePickerservice: EqpDatetimerangepickerService,
private translateService: TranslateService
) {
this.eqpDateTimeRangePickerservice.loadTranslateService(this.translateService);
}
}
export function HttpLoaderFactory(http: HttpClient): TranslateHttpLoader {
return new TranslateHttpLoader(http);
}API
Inputs
| Input | Type | Default | Required | Description |
| --------------------------- | ---------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [type] | PickerModeEnum | PickerModeEnum.DATE | no | Defines the view mode of the picker. DATETIME = 1 DATE = 2 TIME = 3 DATE_RANGE=4 |
| [readonlyInput] | boolean | false | no | Defines the input area as read only. If true the value can be changed only through the calendar. |
| [minDate] | Date, null | null | no | Sets the lowest date that can be inserted. |
| [maxDate] | Date, null | null | no | Sets the highest date that can be inserted. |
| [isRequired] | boolean | false | no | Marks the input as required. Needed only with NgModelInput (with FormControl, Validators.required is enough). |
| [formGroupInput] | FormGroup, null | null | no | FormGroup in which the eqp-datetimerangepicker is used. If not null then formControlNameInput is required. |
| [formControlNameInput] | string, null | null | no | Has effect only if formGroupInput is not null. FormControlName of the control used in the defined formGroupInput. (NOTE: use it without ngModelInput). |
| [formControlNameInputStart] | string, null | null | no | FormControlName used for the start date of the DATE_RANGE picker. (NOTE: use it instead of formControlName without ngModelInput; you also need to specify formControlNameInputEnd). |
| [formControlNameInputEnd] | string, null | null | no | FormControlName used for the end date of the DATE_RANGE picker. (NOTE: use it instead of formControlName without ngModelInput; you also need to specify formControlNameInputStart). |
| [ngModelInput] | Date, string, null | null | no | ngModel to bind the input for all kinds of picker. (NOTE: use it instead of formGroup and formControl binding). |
| [placeholder] | string | DATE: "Seleziona una data", DATETIME: "Seleziona una data e un orario", TIME: "Seleziona un orario" | no | Placeholder viewed in case of DATE, DATETIME or TIME picker. |
| [startPlaceholeder] | string | DATE_RANGE: "Seleziona data inizio" | no | Placeholder viewed in case of DATE_RANGE picker for the start date. (NOTE: use it instead of placeholder; you also need to specify endPlaceholeder). |
| [endPlaceholeder] | string | DATE_RANGE: "fine" | no | Placeholder viewed in case of DATE_RANGE picker for the end date. (NOTE: use it instead of placeholder; you also need to specify startPlaceholeder). |
| [disabled] | boolean | false | no | If true, the picker is readonly and can't be modified. |
| [showSpinners] | boolean | true | no | If true, the spinners above and below the time input are visible. |
| [showSeconds] | boolean | true | no | If true, the seconds field is shown in TIME and DATETIME pickers. |
| [disableMinute] | boolean | false | no | If true, the minute field is readonly. |
| [stepSecond] | number | 1 | no | The number of seconds to add/subtract when clicking the second spinners. |
| [stepHour] | number | 1 | no | The number of hours to add/subtract when clicking the hour spinners. |
| [stepMinute] | number | 1 | no | The number of minutes to add/subtract when clicking the minute spinners. |
| [color] | ThemePalette | undefined | no | Color palette to use on the datepicker's calendar. |
| [enableMeridian] | boolean | false | no | Whether to display 12H (AM/PM) or 24H mode. |
| [touchUi] | boolean | false | no | Whether the calendar UI is in touch mode. In touch mode the calendar opens in a dialog rather than a popup and elements have more padding to allow for bigger touch targets. Note: the component also activates touch mode automatically when the window width is ≤ 700px. |
| [showRangePreset] | boolean | true | no | Whether to display the DATE_RANGE picker presets window. If true, customRangePreset is required. |
| [customRangePreset] | Array<{label: string; orderPosition?: number; getRangeFunction?: () => [Date, Date];}> | [] | no | Array of objects to define the range presets of DATE_RANGE picker. If showRangePreset is true, customRangePreset must be defined. |
| [language] | string | it | no | it or en to specify the locale language for the DATE_RANGE preset labels. |
| [timeType] | TimeTypeEnum | TimeTypeEnum.STRING | no | The type of data returned in the case of TIME mode picker. DATE = 1 returns a Date object; STRING = 2 returns a formatted string (HH:mm:ss or HH:mm). |
| [showButtons] | boolean | false | no | Show the Cancel and Apply buttons inside the picker modal/overlay, deferring value confirmation until Apply is clicked. |
| [cancelButtonText] | string | Cancella | no | Cancel button text inside the modal/overlay. Clicking it clears the current value. |
| [applyButtonText] | string | Applica | no | Apply button text inside the modal/overlay. Clicking it confirms the selected value. |
| [datepickerFilter] | (date: Date) => boolean | null | no | Function applied to every day in the calendar. If it returns true, the day is selectable. NOTE: day 0 is Sunday. For DATE_RANGE, the filter is validated across the entire selected range — if any day in the range fails the filter, the range is automatically cleared. |
| [showTimePopover] | boolean | true | no | Controls how the TIME picker is entered. true: keyboard input is disabled; the time can only be set via the popover overlay. false: the time can only be typed via keyboard (when using formControl, the output type will always be a string). |
| [outputAsString] | boolean | false | no | If true, the emitted value is converted to an ISO-like string (YYYY-MM-DDTHH:mm:ss) instead of a Moment object. For DATE_RANGE, outputs { from: string, to: string }. Useful for REST APIs that expect string dates. |
customRangePreset definition
The DATE_RANGE preset window has some default presets that need only the label property to be used. These default presets have proper orderPosition values and getRangeFunction implementations built in.
| Property | Type | Default | Required | Description |
| ---------------- | --------------------------------- | --------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| label | string | | yes | Text to show on the preset button. If it matches a default preset label, that preset's getRangeFunction is used automatically. |
| orderPosition | number, undefined | undefined | no | Index to specify the position of the preset. To insert a custom preset between default presets, the orderPosition must be between theirs. |
| getRangeFunction | (() => [Date, Date]), undefined | undefined | no | Function executed by the picker to get the [start, end] date pair when the preset button is clicked. Required for custom presets. |
Default preset
| label | orderPosition | | ----------- | ------------- | | Today | 100 | | Last 7 days | 200 | | This week | 300 | | This month | 400 | | This year | 500 | | Last week | 600 | | Last month | 700 | | Last year | 800 |
To include a default preset in your picker, add an entry to customRangePreset with just the matching label. The component will automatically attach the built-in getRangeFunction and orderPosition for it.
Outputs
| Output | Event Arguments | Required | Description |
| -------------------- | ------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------- |
| (ngModelInputChange) | EventEmitter<any> | no | Invoked when the selected value changes and ngModelInput is bound. The output type is the same as the ngModelInput type. |
| (formControlChange) | EventEmitter<any> | no | Invoked when the selected value changes and formGroupInput is bound. |
Model, Interfaces and Enums used
Enums description
| EnumType | Description | Notes |
| ---------------- | ----------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| PickerModeEnum | Define the view mode of the picker. | Has the following values: DATETIME = 1 -> shows a picker to select date and time; DATE = 2 -> shows a date only picker and the returned time of date is set to ("00:00:00"); TIME = 3 -> shows a time only picker which returns the selected time; DATE_RANGE = 4 -> shows a date only picker where can be selected the initial and the end date of a range, and returns the start and the end dates with time ("00:00:00"); |
| TimeTypeEnum | Used to define the type of data returned in the case of TIME mode | DATE = 1 returns a Date object set to today with the selected time; STRING = 2 returns a string formatted as HH:mm:ss (or HH:mm when showSeconds is false). |
export enum PickerModeEnum {
DATETIME = 1,
DATE = 2,
TIME = 3,
DATE_RANGE = 4
}export enum TimeTypeEnum {
DATE = 1,
STRING = 2,
}Notes on behavior
UTC normalization
All DATE and DATETIME values are internally converted to UTC via moment.utc(true) before being emitted. This means the local time value is preserved but the timezone offset is set to zero — the date is not shifted. For example, selecting 2024-06-15 10:30 will emit 2024-06-15T10:30:00Z, not a UTC-shifted equivalent.
DATE_RANGE end-date boundary
In DATE_RANGE mode, the end date (to) is automatically adjusted to the last millisecond of that day (+1 day −1ms). This makes the range boundary inclusive of the entire selected end day. For example, selecting June 1 → June 7 emits from: 2024-06-01T00:00:00Z, to: 2024-06-07T23:59:59.999Z.
Responsive touch mode
The component listens to window:resize events. When the window width drops to 700px or below, touchUi is automatically set to true regardless of the value passed in. This renders the calendar as a centered modal dialog rather than an inline popup, improving usability on mobile. If you explicitly set [touchUi]="true", the component will not override it.
TIME picker overlay
The TIME picker (PickerModeEnum.TIME) renders a ngb-timepicker inside an Angular CDK Overlay panel. The overlay is positioned below the trigger field and closes automatically when:
- The user clicks outside the panel.
- The backdrop is clicked (in touch/modal mode).
- The
Escapekey is pressed.
When showTimePopover is false, the overlay is not used and the time must be typed directly into the <input type="time"> field via keyboard.
Automatic isRequired detection
When using formGroupInput, the component reads the validators on the bound FormControl at each change detection cycle. If Validators.required is present, isRequired is set to true automatically — you do not need to pass [isRequired]="true" explicitly.
outputAsString mode
When [outputAsString]="true" is set, all emitted values are serialized to the string format YYYY-MM-DDTHH:mm:ss using Moment.js. This is useful when the bound model is a string (e.g., from a REST API response) and you want a round-trip without type conversion. For DATE_RANGE mode, the output is { from: 'YYYY-MM-DDTHH:mm:ss', to: 'YYYY-MM-DDTHH:mm:ss' }.
Examples & Use cases
It is highly recommended not to use ngModelInput together with formControl - formGroup
Using ngModelInput (type = DATE)
<eqp-datetimerangepicker
[minDate]="minDate"
[maxDate]="maxDate"
[type]="PickerModeEnum.DATE"
[disabled]="disable"
[(ngModelInput)]="date"
[isRequired]="isRequired"
[placeholder]="''"></eqp-datetimerangepicker>PickerModeEnum = PickerModeEnum;
minDate: Date = new Date(); // minimum date selectable
maxDate: Date = null; // maximum date selectable (ex. last day of this year)
disable: boolean = false; // flag to set the input disabled
date: Date | null = null;Using ngModelInput (type = TIME)
<eqp-datetimerangepicker
[type]="PickerModeEnum.TIME"
[timeType]="TimeTypeEnum.DATE"
[disabled]="disable"
[(ngModelInput)]="timePickerInput"
[placeholder]="''"
[isRequired]="isRequired"
[showSeconds]="showSeconds"></eqp-datetimerangepicker>PickerModeEnum = PickerModeEnum;
TimeTypeEnum = TimeTypeEnum;
timePickerInput: Date | string | null = null;
showSeconds: boolean = true; // flag to allow second selection from the pickerUsing ngModelInput (type = DATE_TIME)
<eqp-datetimerangepicker
[minDate]="minDate"
[maxDate]="maxDate"
[type]="PickerModeEnum.DATETIME"
[disabled]="disable"
[(ngModelInput)]="dateTimePickerWithTime"
[placeholder]="''"
[isRequired]="isRequired"
[showSeconds]="showSeconds"></eqp-datetimerangepicker>PickerModeEnum = PickerModeEnum;
dateTimePickerWithTime: Date | null = null;
showSeconds: boolean = true; // flag to allow second selection from the pickerUsing ngModelInput (type = DATE_RANGE)
<eqp-datetimerangepicker
[customRangePreset]="customRangePreset"
[minDate]="minDate"
[maxDate]="maxDate"
[type]="PickerModeEnum.DATE_RANGE"
[disabled]="disable"
[(ngModelInput)]="range"
[startPlaceholeder]="''"
[endPlaceholeder]="''"
[isRequired]="isRequired"
[showRangePreset]="showRangePreset"
[language]="presetLanguage"></eqp-datetimerangepicker>PickerModeEnum = PickerModeEnum;
range: {from: Date | null, to: Date | null} = { from: null, to: null };
showRangePreset: boolean = true // flag to show the preset window
presetLanguage: string = "en"
getTwoDayAgoToNow: (() => [Date, Date]) | undefined = () => [
new Date(new Date().setDate(new Date().getDate() - 2)),
new Date()
];
customRangePreset = [
{
label: "Tow days ago",
orderPosition: 150, // to show this preset between Today and Last year
getRangeFunction: this.getTwoDayAgoToNow //function to be executed that rerun [Date, Date]
}, // custom preset
{
label: "Today"
}, // default presets to be used
{
label: "Last year"
},
{
label: "This month"
},
{
label: "Last week"
},
{
label: "This week"
}
];Using formControl (type = DATE)
<eqp-datetimerangepicker
[minDate]="minDate"
[maxDate]="maxDate"
[type]="PickerModeEnum.DATE"
[disabled]="disable"
[formGroupInput]="formGroup"
formControlNameInput="dateControl"
[isRequired]="isRequired"
[placeholder]="''"></eqp-datetimerangepicker>Using formControl (type = TIME)
<eqp-datetimerangepicker
[type]="PickerModeEnum.TIME"
[timeType]="TimeTypeEnum.STRING"
[disabled]="disable"
[formGroupInput]="formGroup"
formControlNameInput="timePickerInputControl"
[placeholder]="''"
[isRequired]="isRequired"
[showSeconds]="showSeconds"></eqp-datetimerangepicker>Using formControl (type = DATE_TIME)
<eqp-datetimerangepicker
[minDate]="minDate"
[maxDate]="maxDate"
[type]="PickerModeEnum.DATETIME"
[disabled]="disable"
[formGroupInput]="formGroup"
formControlNameInput="dateTimePickerWithTimeControl"
[placeholder]="''"
[isRequired]="isRequired"
[showSeconds]="showSeconds"></eqp-datetimerangepicker>Using formControl (type = DATE_RANGE)
<eqp-datetimerangepicker
[customRangePreset]="customRangePreset"
[minDate]="minDate"
[maxDate]="maxDate"
[type]="PickerModeEnum.DATE_RANGE"
[disabled]="disable"
[formGroupInput]="formGroup"
formControlNameInputStart="dateRangeControlStart"
formControlNameInputEnd="dateRangeControlEnd"
[startPlaceholeder]="''"
[endPlaceholeder]="''"
[isRequired]="isRequired"
[showRangePreset]="showRangePreset"
[language]="presetLanguage"></eqp-datetimerangepicker>//#region time picker fields
TimeTypeEnum = TimeTypeEnum;
timePickerInput: Date | string | null = null;
//#endregion
PickerModeEnum = PickerModeEnum;
formGroup = new FormGroup({
timePickerInputControl: new FormControl(this.timePickerInput),
dateTimePickerWithTimeControl: new FormControl(this.dateTimePickerWithTime),
dateRangeControlStart: new FormControl(this.range?.from),
dateRangeControlEnd: new FormControl(this.range?.to),
dateControl: new FormControl(this.date)
});Using outputAsString with a DATE_RANGE and REST API
When working with backend APIs that exchange ISO date strings, set [outputAsString]="true" to receive and emit string values instead of Moment objects.
<eqp-datetimerangepicker
[type]="PickerModeEnum.DATE_RANGE"
[outputAsString]="true"
[(ngModelInput)]="apiDateRange"
[customRangePreset]="customRangePreset"
[showRangePreset]="true"
[language]="'en'"></eqp-datetimerangepicker>PickerModeEnum = PickerModeEnum;
// Can be pre-populated with strings from an API response
apiDateRange: { from: string | null, to: string | null } = {
from: '2024-01-01T00:00:00',
to: '2024-01-31T23:59:59'
};
// The emitted value will also be { from: 'YYYY-MM-DDTHH:mm:ss', to: 'YYYY-MM-DDTHH:mm:ss' }
customRangePreset = [
{ label: "This month" },
{ label: "Last month" },
{ label: "This year" }
];Using datepickerFilter to disable weekends
<eqp-datetimerangepicker
[type]="PickerModeEnum.DATE"
[(ngModelInput)]="date"
[datepickerFilter]="noWeekends"></eqp-datetimerangepicker>PickerModeEnum = PickerModeEnum;
date: Date | null = null;
// Day 0 = Sunday, Day 6 = Saturday
noWeekends = (date: Date): boolean => {
const day = date.getDay();
return day !== 0 && day !== 6;
};Note: When used with
DATE_RANGE, the filter is applied to every date in the selected range. If the user selects a range that includes a disabled day, the entire range is automatically cleared.
Using showButtons to defer confirmation
Setting [showButtons]="true" adds Cancel and Apply buttons to the picker overlay/modal. The value is only emitted when Apply is clicked. Clicking Cancel clears the current value.
<eqp-datetimerangepicker
[type]="PickerModeEnum.DATETIME"
[(ngModelInput)]="dateTime"
[showButtons]="true"
[cancelButtonText]="'Clear'"
[applyButtonText]="'Confirm'"></eqp-datetimerangepicker>Credits
This library has been developed by EqProject SRL, for more info contact: [email protected]
