Angular material has a very nice date picker but while its functionality can accommodate a lot of developers, sometimes it just falls short. For instance, I find it very inflexible when dealing with date formats. While I understand that it’s nice to have a common date format for the whole application, sometimes you need to be able to set date formats on the fly without breaking your back. Sometimes all you need is a simple solution that is ready to be used anywhere in your application.

Introduction

This tutorial assumes you have some knowledge in Angular and Angular Material, because I won’t go into details on the basics like setting up modules. You can learn more about Angular here and Angular Material here. This tutorial has been tested with Angular 6, but I expect it to work with Angular 5 as well but not any earlier version than that.

We will be using ControlValueAccessor from @angular/forms to create a custom form component that will wrap around Angular Material Date Picker component. The new component will extend the Material Date Picker component and allow you to pass a date format in which to use. To format the date, we will use MomentJS which you can learn more about here. Feel free to use a different library or rely with JavaScript/Typescript in build methods.

In the demo for this tutorial, we will have multiple Date Pickers that give back different date formats. To simplify this tutorial, I will ignore all properties of Angular Material Date Pickerthat are optional like minimum and maximum date. This can easily be remedied by passing them as options in your component using the Input() method. This method allows parent components to pass variables to child component, you can learn more about this here.

You can play with this tutorial demo here.

Getting Started

To get started, you need to install (using either NPM or Yarn) the following packages: momentjs, angular-material, angular-flex-layout and material-moment-adapter inside your angular application. You can install them using Yarn in one command as shown below:

yarn add –dev @angular/material moment @angular/material-moment-adapter @angular/flex-layout

For those using Angular 6, you can install angular material using the new ng new command:

ng add @angular/material

Finally import the following in your main NgModules file:

FormsModule,
ReactiveFormsModule,
BrowserAnimationsModule,
MatDatepickerModule,
MatInputModule,
MatFormFieldModule,
FlexLayoutModule,
MatCardModule

On top of Angular Material, I am using Angular Flex Layout, which you can learn more about here.

Create Custom Date Picker Component

Next, create a new component – date-picker-component – using Angular CLI.

ng new component date-picker

This component will have our Material Date Picker inside it but will read and format the date as we want. It will also accept date input incase we have an initial date or are setting it programmatically from an API. Inside the component typescript file, we are going to implement ControlValueAccessor:

export class DatePickerComponent implements ControlValueAccessor {}

Then, we are going to add a provider for the component, as shown below:

providers: [
  {
    provide: NG_VALUE_ACCESSOR,
    useExisting: forwardRef(() => DatePickerComponent), //Name of our component
    multi: true
  }
];

NB: Remember to import remember the necessary imports for the component as shown below:

import { Component, forwardRef, Input } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import { MatDatepickerInputEvent } from '@angular/material/datepicker';
import * as moment from 'moment';

Inside our component, we need to add a get and set method for the value of the date. We start by declaring a property of the form _dateValue (note the preceding underscore) and then creating the two methods to set and get the value of the _dateValue property:

@Input() _dateValue: string = null;

get dateValue() {
    return moment(this._dateValue, this.format);
}

set dateValue(val) {
    this._dateValue = moment(val).format(this.format);
    this.propagateChange(this._dateValue);
}

On top of those two methods, you need the following methods – which I won’t go into details about:

addEvent(type: string, event: MatDatepickerInputEvent<Date>) {
    this.dateValue = moment(event.value, this.format);
}

writeValue(value: any) {
    if (value !== undefined) {
      this.dateValue = moment(value, this.format);
    }
}

propagateChange = (_: any) => {};

registerOnChange(fn) {
    this.propagateChange = fn;
}

registerOnTouched() {}

We also need a placeholder and format property in our component. The placeholder will be passed to the Material Date Picker Component as a placeholder text. While format property will be used to format the value of the Date Picker when getting.

@Input() public placeholder: string = null;
@Input() private format = 'YYYY/MM/DD HH:mm:ss';

And finally, to our component template, where we will add a normal Material Date Picker input field:

<mat-form-field fxFlex="100">
  <mat-datepicker-toggle matPrefix [for]="picker"></mat-datepicker-toggle>
  <input
    matInput
    [matDatepicker]="picker"
    [value]="dateValue"
    (dateInput)="addEvent('input', $event)"
    [placeholder]="placeholder"
  />
  <mat-datepicker #picker></mat-datepicker>
</mat-form-field>

And that’s it, you have now a custom date picker that you can use anywhere in your application:

<app-date-picker
  placeholder="Enter The First Date"
  [(ngModel)]="firstDate"
  format="YYYY/MM/DD HH:mm:ss"
></app-date-picker>

You can find the above code on github, here is the link. If you have any questions, please leave it on the comment section below.