Daniel Salvagni — Yet another software engineer
The sweet road of building a custom date picker
Recently I had to replace a date picker component in an Angular application and I decided, along with my teammates, to go with a custom solution. The previous date picker we’ve been using was a component from ngx-bootstrap library, which is a great one.
The decision of building a custom one was taken mostly because we need mobile support, and we didn’t find a solution off-the-shelf that would fit our requirements. It wasn’t an easy decision, a calendar is always tricky to build. Although, on our side, there was a narrow range of use cases that would make it simpler than building a date picker for global usage.
I started looking for an open-source date picker component that would suit our needs and, since we needed support to a range of dates, there wasn’t easy to find a ready-to-use. I tried at first styling and customizing the date picker from ngx-bootstrap, but the results were suboptimal. One issue that I couldn’t not workaround properly was the double-tap to select a date on iOS. The suggestion proposed on the GH issue isn’t ideal, as it changes a private* method of this component. I made enough changes to make this component mobile-ready to our use case and left it aside. It would have been great if I could only change the theme without having to replace the implementation, and that’s why I choose to first give our (at the moment) current one a chance.
My second approach was looking at Angular Material components. They offer a date picker, however without range selection support, as of now. They are planning to release a date picker with range selection at the end of Q1/2020. I couldn’t wait and soon dropped the idea.
The third library that I tried out was flatpickr, that offers a lightweight date picker written in JavaScript. I approach this solution the same way I did with ngx-bootstrap date picker. Since this component is not an Angular component, it was needed to create a wrapper for it, instantiate it and display it inside of modal (CdkOverlay). I still needed to create directives to open it from any element and an input date picker for it. As soon as I got a working solution, I left it aside to go further with another attempt.
All these three approaches with the off-the-shelf solutions were my fallback. I would invest the time that I had remaining to build a custom one, that was agreed upon within the team.
Now, when you decide to build a custom component, you have the freedom to build it as you desire. If that in mind, I started evaluating different calendar approaches from other developers and teams. The first attempt was to build an infinite scrolling calendar like the experience iOS gives you with the calendar on the iPhone. I got inspired by this react-infinite-calendar and decided to write something similar in Angular.
Angular CDK is super handy. They offer solutions both for overlay & positioning and virtual scrolling. It was enough to start building custom calendar #1 using Angular CDK solutions.
I built a similar calendar and got an huge help from this article to make it possible. I tried first with the ready-to-use solution from CDK and later had to write a custom strategy for virtual scrolling. Both approaches gave suboptimal results on mobile (Samsung Galaxy A6+ as base device for testing). The main issue was that the virtual scrolling expects you to inform the size of the item in the list. It should be fixed. What happens is that we don’t have regular month sizes in our calendar and the count of weeks (consequently rows) changes from one month to another. Even with the custom strategy, where I would set dynamically the size of the content, I couldn’t make it work properly on mobile. Although this is something that I want to look at with more time in the near future, I decided to drop this idea for the current problem we are trying to solve: a mobile-ready date picker.
The final solution was building a custom date picker with the regular side-by-side month calendar. It works great. It’s fast, I’m not using any third-party library (looking at you Moment) to generate the calendar. Basically, it’s a pure Angular component. I’m going to give a brief description of its features, built according to our expectations and requirements.
Single and range selection
We need to be able to select one date or a range of dates.
Date limits
We need to be able to set a minimum and maximum selectable date.
Custom ranges
We need to be able to select a custom range of dates by clicking on a shortcut button (last 7 days).
Week starts at…
We must be able to define the week starting date.
Must be localized
We must offer it both in German and English. Used Angular Locales and its handy date functions to do it.
Inline on desktop, fullscreen on mobile
Have you tried to select a date on a date picker built for desktop only, on a mobile device? If so, you get this.
Calendar, input component, directives, and ControlValueAssessor
You know, everything you expect from a form element in an Angular application.
Although I said at the beginning of this post that building calendars are never an easy task, it can become easier when you narrow your requirements to the real use cases of your product. Does this custom component work for every product? No. Does it cover everything such as the ngx-bootstrap or the flatpickr components? No. Does it support 25+ languages? No. Does it manipulate time, intervals and timezone? No, thank god. And it’s fine.
The problems with working with dates are always the exceptions that we need to be aware of, especially when manipulating dates and working with timezone. All these issues were, fortunately, not in the scenarios of the component I built. I need a calendar that works from 2010 to 2030, and that’s all. That’s what simplified the things during this process.
In the past, I was always more inclined to use ready-to-use libraries of components instead of building custom ones. Since a couple of years ago, this line of thought changed. The main reason that changed my mind, is that I believe we shouldn’t get used to the chaos. We shouldn’t get used to picking solutions that are not ideal, for the simple reason that they are ready, would save your time. The cost of having an unhappy user is way higher than the time investing in building something that they are going to love to use. Don’t let your UI staying in the way of a user that wants to achieve a task just for the sake of releasing it fast.
In the end, there are two reasons people are going to use your web app: spend and saving time. As a software engineer, unless you are working in the entertainment industry (hey Netflix), your ultimate goal is to save people’s time.
(*) JavaScript has no real private methods, they are always accessible, but it’s a bad practice changing it. I mean, they are private for a reason, right?