Taking Advantage of the Angular Material Datepicker
The datepicker is one of the newest components in Angular Material (added in May 2017). Our goal in creating the Angular Material datepicker is to create a datepicker that adheres to Material Design principles and is flexible enough to work across different applications and in different locales. The datepicker is still a relatively new component and there is plenty more work to be done, but we believe that the current design is a solid foundation to build on.
Overview
The datepicker consists of three main parts.
- The input
- The popup trigger
- The calendar popup
This is reflected in the API:
The md-datepicker
component is the core of the datepicker, we then connect an Input to it using the [mdDatepicker]
directive. The md-datepicker-toggle
is completely optional. It’s an easy way to create a md-icon-button
and automatically hook it up to the datepicker, but you can always create a custom toggle by connecting the (click)
event on any element to call picker.toggle()
.
The datepicker input supports the usual min
, max
, and required
attributes to add validation as well as an additional mdDatepickerFilter
attribute which allows for finer grained control of what’s considered a valid date. Adding validation to the input also automatically disabled invalid dates on the calendar.
The md-datepicker
element has a startDate
attribute to set the date the calendar starts at and a startView
attribute that can be used to set the initial calendar view to either month
or year
mode.
To improve usability on mobile devices where screen real estate is limited there is a boolean attribute, touchUi
, that can be used to enable or disable a touch UI mode. Currently the only difference is that the touch UI mode opens in a fullscreen dialog whereas the normal mode opens in a popup attached to the input. In the future we expect these UIs to diverge further as we continue to optimize each mode for its respective use case. Rather than prescribing what counts as a touch device, the datepicker leaves this decision up the developer. We recommend setting up a dynamic binding: [touchUi]="isTouch"
.
i18n
One of the most challenging aspects of designing a datepicker is making sure that it works in all locales. This is difficult because different locales may have different date formats, calendar conventions (e.g. which day is the first day of the week), or even completely different calendars.
Because of the differences in date format across locales, even choosing a date representation is difficult. The native JavaScript Date
object offers very limited parsing capabilities. For example, regardless of your locale, 1/2/2017
will always be parsed as January 2nd, 2017
, even though in many locales it actually means February 1st, 2017
.
Because of these limitations we can’t always rely on the native Date
object. Maintaining our own date parsing and formatting logic is outside the scope of Angular Material. At the same time we don’t want to introduce a dependency on a third party date library, this could bloat people’s apps, and they may already have a date solution they’re happy with.
The DateAdapter
Our solution to this problem was to allow developers to choose the date representation that works best for them. We accomplished this by removing all knowledge of how dates actually work from the datepicker and moving it instead into a DateAdapter
class. DateAdapter
is an abstract class that requires a concrete subclass to actually fill in the details of to work with dates. Angular Material includes one such subclass NativeDateAdapter
. By providing the NativeDateAdapter
as the application’s DateAdapter
the datepicker can be made to work with the native JavaScript Date
. However this means that your application will have the date parsing limitations discussed above. If your application needs to work with locales other than en-US
, we recommend creating your own adapter instead, by extending either DateAdapter
or NativeDateAdapter
.
One of the coolest things about the DateAdapter
design is that it allows the entire datepicker to be generic and work with any kind of date object you like. For example, say you’re already using Moment.js throughout your app, you can create a MomentDateAdapter
:
Specify the formats to use for various pieces of the datepicker (e.g. the month header that normally says something like “Jul 2017” and the parsing format for the input):
And finally provide both of these things in your application module:
Now when you go to use the datepicker, instead of passing it Date
objects you just directly pass it Moment
objects:
Future work
As the datepicker has only just been released there are still a number of features we want to add:
- Additional pre-made date adapters for libraries like Moment.js, date-fns #5972
- Infinite scrolling through months/years #5973
- Ability to select a month rather than a specific date #4853
- Ability to select a date & time #5648
- Ability to select a range of dates #4763
- Support for non-Gregorian calendars #2519
- General UX enhancements #5974
Conclusion
To try it out, go grab the latest version of Angular Material from npm and check out the datepicker documentation. The quickest way to get up and running is to import the MdNativeDateModule
in your app’s root module. This will allow the datepicker to work with native Date
objects, but beware the MdNativeDateModule
doesn’t support input formats other than mm/dd/yyyy
. If you want it to work in locales that use other formats you’ll probably want to write your own DateAdapter or wait for the official Moment.js or date-fns DateAdapters. We also have plans to significantly improve the native date experience using Angular’s upcoming new i18n API (#6030).