Kevin Curnow
Kevin Curnow

Reputation: 989

Angular-Material DateTime Picker Component?

I imported a date picker in a project and was wondering if there was any official recent component from angular and material to include time in the calendar as well.
I've seen plenty of time pickers in material documentation and researched a lot of third party ones, but they seem very complex.

Upvotes: 98

Views: 261956

Answers (11)

Maurice
Maurice

Reputation: 7361

You can have a datetime picker when using matInput with type datetime-local like so:

<mat-form-field>
  <input matInput type="datetime-local" placeholder="start date">
</mat-form-field>

You can click on each part of the placeholder to set the day, month, year, hours,minutes and whether its AM or PM.

Upvotes: 99

user28719671
user28719671

Reputation: 11

everyone! The simplest and easiest way to implement a date-time picker is by using the input element with the datetime-local type. I highly recommend trying it out. Creating a custom component or using third-party libraries can be quite complicated due to compatibility issues with the latest Angular versions.

Upvotes: 1

PIer
PIer

Reputation: 101

Material has finally released the datetime component some days ago in v19 :) https://github.com/angular/components/issues/5648 is closed.

For those who have to deal with datetime picker in Angular 18 in the meantime, my approach was to switch to @4sellers/angular-material-components-datetime-picker which is a fork of @angular-material-components/datetime-picker but "compliant" with Angular 18. And some styles adjustments mentionned here : https://github.com/h2qutc/angular-material-components/issues/348#issuecomment-2041684641

But as soon as I'm going on Angular 19, I'll switch to the new fancy Material datetime picker

Upvotes: 0

Pedro Bezanilla
Pedro Bezanilla

Reputation: 1365

Currently there's an official Open Issue against Angular Team since 4 years ago requesting to support Time and DateTime picker: https://github.com/angular/components/issues/5648

There are plenty of libraries out there as you can see on this post, problem is most of them are deprecated (older Angular versions).

So, I'm actually using this one from @matheo, it's the most recent and maintained I've found:

https://www.npmjs.com/package/@coachcare which was moved recently to https://www.npmjs.com/package/@matheo/datepicker


Demo: http://matheo.co/demos/datepicker



NOTE: If it doesn't work for Angular version >= 12 please check that new Angular convention theming is being used, e.g.:

@use "~@matheo/datepicker/theming" as datepicker;
// ...
@include datepicker.mat-datepicker-theme($main_theme);

MARCH 2024 EDIT:

I wanted to get rid of Moment dates in order to work with Date and date-fns library instead, so I moved to this datepicker library which works pretty well:

https://ng-matero.github.io/extensions/components/datetimepicker/overview

Upvotes: 7

Behnam Asaei
Behnam Asaei

Reputation: 353

another way use: mat-datepicker-actions

{{label}}
    <mat-datepicker #picker>
      <mat-datepicker-actions>
        <input [ngxTimepicker]="pickerTime">
        <ngx-material-timepicker #pickerTime></ngx-material-timepicker>
      </mat-datepicker-actions>
    </mat-datepicker>
  </mat-form-field>

enter image description here

Upvotes: -2

Samar Haider
Samar Haider

Reputation: 912

I think better to use angular material extension. It seems to be very useful https://ng-matero.github.io/extensions/components/datetimepicker/overview

Upvotes: 4

Mallesh Gali
Mallesh Gali

Reputation: 41

The best way to implement Date&Time control is using input type "datetime-local". Please refer https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/datetime-local

sample code is

<mat-grid-list [cols]="5" gutterSize="20px" rowHeight="70px">
                <form *ngIf="dateForm && dateForm !== null" [formGroup]="dateForm">
                    <mat-grid-tile>
                        <mat-form-field appearance="outline" floatLabel="auto">
                            <mat-label>From Date</mat-label>
                            <input type="datetime-local" matInput name="fromTime" formControlName="fromDate">
                        </mat-form-field>
                    </mat-grid-tile>
                    <mat-grid-tile>
                        <mat-form-field appearance="outline" floatLabel="auto">
                            <mat-label>To Date</mat-label>
                            <input type="datetime-local" matInput name="toTime" formControlName="toDate">
                        </mat-form-field>
                    </mat-grid-tile>
                </form>
</mat-grid-list>

Upvotes: 4

hqho
hqho

Reputation: 634

I recommend you to checkout @angular-material-components/datetime-picker. This is a DatetimePicker like @angular/material Datepicker by adding support for choosing time.

enter image description here

Upvotes: 45

vlio20
vlio20

Reputation: 9295

I would suggest you to checkout https://vlio20.github.io/angular-datepicker/

enter image description here

Upvotes: 1

CularBytes
CularBytes

Reputation: 10321

For as long as there is no official date and time picker from angular itself, I would advise to make a combination of the default angular date picker and this Angular Material Timepicker. I've chosen that one because all the other ones I found at this time lack support for issues, are outdated or are not functioning well in the most recent angular versions. This guy seems to be very responsive.

I've wrapped them both in one component so that it looks like it is one unit. You just have to make sure to do a few things:

When no input has been given yet, I would advise:

  • On-click of the component, the datepicker should always be triggered first.
  • After the datepicker closes, have the timepicker automatically popup.
  • Use touchUi = true on the datepicker, so that both the datepicker and the timepicker come as a dialog after each other.
  • Make sure the datepicker also shows up when clicking on the form (instead of only on the icon which is the default).
  • Use this solution to use the timepicker also in a material form, when placing them behind each other, it looks like it is one form.

After a value has been given, it is clear that one part contains the time and the other part contains the date. At that moment it is clear that the user has to click on the time to change the time, and on the date to change the date. But before that, so when both fields are empty (and 'attached' to each other as one field) you should make sure the user cannot be confused by doing above recommendations.

My component is not complete yet, I will try to remember myself to share the code later. Shoot a comment if this question is more then a month old or so.

Edit: Result

enter image description here

<div fxLayout="row">
  <div *ngIf="!dateOnly" [formGroup]="timeFormGroup">
    <mat-form-field>
      <input matInput [ngxTimepicker]="endTime" [format]="24"  placeholder="{{placeholderTime}}" formControlName="endTime" />
    </mat-form-field>
    <ngx-material-timepicker #endTime (timeSet)="timeChange($event)" [minutesGap]="10"></ngx-material-timepicker>
  </div>
  <div>
    <mat-form-field>
      <input id="pickerId" matInput [matDatepicker]="datepicker" placeholder="{{placeholderDate}}" [formControl]="dateForm"
             [min]="config.minDate" [max]="config.maxDate" (dateChange)="dateChange($event)">
      <mat-datepicker-toggle matSuffix [for]="datepicker"></mat-datepicker-toggle>
      <mat-datepicker #datepicker [disabled]="disabled" [touchUi]="config.touchUi" startView="{{config.startView}}"></mat-datepicker>
    </mat-form-field>
  </div>
</div>
import { Component, OnInit, Input, EventEmitter, Output } from '@angular/core';
import { FormControl, FormGroup } from '@angular/forms';
import { DateAdapter, MatDatepickerInputEvent } from '@angular/material';

import * as moment_ from 'moment';

const moment = moment_;

import { MAT_MOMENT_DATE_ADAPTER_OPTIONS } from '@angular/material-moment-adapter';

class DateConfig {
  startView: 'month' | 'year' | 'multi-year';
  touchUi: boolean;
  minDate: moment_.Moment;
  maxDate: moment_.Moment;
}

@Component({
  selector: 'cb-datetimepicker',
  templateUrl: './cb-datetimepicker.component.html',
  styleUrls: ['./cb-datetimepicker.component.scss'],
})
export class DatetimepickerComponent implements OnInit {

  @Input() disabled: boolean;
  @Input() placeholderDate: string;
  @Input() placeholderTime: string;
  @Input() model: Date;
  @Input() purpose: string;
  @Input() dateOnly: boolean;

  @Output() dateUpdate = new EventEmitter<Date>();

  public pickerId: string = "_" + Math.random().toString(36).substr(2, 9);

  public dateForm: FormControl;
  public timeFormGroup: FormGroup;
  public endTime: FormControl;

  public momentDate: moment_.Moment;
  public config: DateConfig;

  //myGroup: FormGroup;


  constructor(private adapter : DateAdapter<any>) { }

  ngOnInit() {

    this.adapter.setLocale("nl-NL");//todo: configurable
    this.config = new DateConfig();
    if (this.purpose === "birthday") {
      this.config.startView = 'multi-year';
      this.config.maxDate = moment().add('year', -15);
      this.config.minDate = moment().add('year', -90);
      this.dateOnly = true;
    } //add more configurations
    else {
      this.config.startView = 'month';
      this.config.maxDate = moment().add('year', 100);
      this.config.minDate = moment().add('year', -100);
    }


    if (window.screen.width < 767) {
      this.config.touchUi = true;
    }



    if (this.model) {
      var mom = moment(this.model);
      if (mom.isBefore(moment('1900-01-01'))) {
        this.momentDate = moment();
      } else {
        this.momentDate = mom;
      }
    } else {
      this.momentDate = moment();
    }

    this.dateForm = new FormControl(this.momentDate);
    if (this.disabled) {
      this.dateForm.disable();
    }
    this.endTime = new FormControl(this.momentDate.format("HH:mm"));

    this.timeFormGroup = new FormGroup({
      endTime: this.endTime
    });


  }


  public dateChange(date: MatDatepickerInputEvent<any>) {

    if (moment.isMoment(date.value)) {
      this.momentDate = moment(date.value);
      if (this.dateOnly) {
        this.momentDate = this.momentDate.utc(true);
      } 
      var newDate = this.momentDate.toDate();
      this.model = newDate;
      this.dateUpdate.emit(newDate);
    }

    console.log("datechange",date);
  }

  public timeChange(time: string) {

    var splitted = time.split(':');
    var hour = splitted[0];
    var minute = splitted[1];

    console.log("time change", time);
   this.momentDate = this.momentDate.set('hour', parseInt(hour));
    this.momentDate = this.momentDate.set('minute', parseInt(minute));

    var newDate = this.momentDate.toDate();
    this.model = newDate;
    this.dateUpdate.emit(newDate);
  }
}

One important source: https://github.com/Agranom/ngx-material-timepicker/issues/126

I think it still deserves some tweaks, as I think it can work a bit better when I would have more time creating this. Most importantly I tried to solve the UTC issue as well, so all dates should be shown in local time but should be sent to the server in UTC format (or at least saved with the correct timezone added to it).

Upvotes: 35

JeremyW
JeremyW

Reputation: 5272

Unfortunately, the answer to your question of whether there is official Material support for selecting the time is "No", but it's currently an open issue on the official Material2 GitHub repo: https://github.com/angular/material2/issues/5648

Hopefully this changes soon, in the mean time, you'll have to fight with the 3rd-party ones you've already discovered. There are a few people in that GitHub issue that provide their self-made workarounds that you can try.

Upvotes: 29

Related Questions