How to build a datepicker with Angular, Bulma and Moment.js (part 1)
Halfway through building my first paid-for web app using Angular, which entails some booking functionalities, I realized that enabling the user to pick a date isn’t something trivial. Especially if some dates are available and others aren’t depending on existing bookings and other complicated business logic. So no, the default HTML5 datepicker doesn’t cut it. At all. After giving up the hope of understanding other people’s code (such as this, or this, or this), after feeling very depressed for a couple of very long hours, and after reading through Moment.js documentation, I took the resolution of building my own Angular datepicker. To keep myself motivated I will write a tutorial “live”, as I pour code out of my brain. Ready? Let’s do it.
1 — Set up yourself
Sit somewhere comfortable, or stand by a standing desk, have your laptop on, with Angular installed, internet access, switch off the mobile and play this tune in your headset. We’re cool, let’s get started.
2 — Set up the project
Create a new Angular project from the terminal:
ng new anguluar-bulma-datepicker
Move to the project folder and create a new component:
ng g c datepicker
Then install Bulma and Moment.js:
npm install --save bulma moment
Launch the code editor, and as a first thing enable bulma css by adding this line “../node_modules/bulma/css/bulma.css" in the “styles” array in the angular-cli.json file. Like so:
Then import Moment.js in your datepicker.component.ts:
Now we can use Bulma and Moment.js in our project. But we could also take advantage of another extension called Bulma Calendar, specifically of it’s CSS code, so to add a proper calendar style to our datepicker out of the box. To do that we need to grab this code from GitHub and save it in a file called bulma-calendar.min.css within our src folder and then make it available by adding another line in our angular-cli.json:
And we’re all set.
3 — Add the necessary HTML to display a datepicker
Once again, we’ll use the excellent code provided by Wikiki in the Bulma Calendar documentation. We can copy the code from the first example in this page and past it in our datepicker.component.html (nobody is watching…):
Oh, it uses FontAwesome. You need the CDN link in your index.html.
If we ng serve the app, at this point we should have a nice looking datepicker, where everything we see is hardcoded in the HTML. Now, let’s turn that in to a real and fully functional datepicker, adding one functionality at the time.
4 — Month, year and navigation
To begin with, let’s display the current month and year. To do that we create a Moment.js object (i.e. a date) having the current date as its value and assign it to a variable (viewDate); also, to make sure we are consistent with the locale settings, we specify the locale as a string (it will later be an Input property of our component):
Sweet. Since now we have the current date, we can replace the hardcoded “March 2017” with the actual month and year. Moment.js makes it so easy to format dates, that all we need to type in our template is {{viewDate.format(‘MMMM YYYY’)}}:
Easy!! Now, when I click on the chevron icons I’m supposed to display the previous or the next month, right? Let’s add a click event and a function to handle it:
The function changeViewDate will take two arguments: a number to indicate the quantity to change and a string to indicate which part of the date should be changed. This way we’re aligned with how things work in Moment.js, which once again makes it so easy to add or subtract intervals from dates. The Moment.js code to add a month:
viewDate.add(1, 'month');
Almost silly. So our changeViewDate will be like this:
It works!! So now we can navigate through time, month by month, using the chevron buttons. But what if for any reason we want to limit how back or forward in time the user can navigate? For example, we shouldn’t let the user select a date in the past for our booking system, or we might want to take bookings only for the next year. To allow for such restrictions we can pass the new date through a validity check, say we create a function canChange which returns a boolean according to some logic. We only need to keep in mind that moment.js objects are mutable, so unless we want to change our viewDate variable we have to clone it in order to manipulate it safely. To clone a date object in moment.js:
const clonedDate = moment(viewDate);
To check if a date is within a range of dates:
clonedDate.isBetween(minDate, maxDate);
where minDate and maxDate are other date objects representing the boundaries of our interval. The check is exclusive so if we want to include the extremes we need to provide larger bounds than we actually want (i.e. checking if ‘today’ is between ‘today’ and ‘next year’ will return false). Wrapping up these considerations of cloning and date comparisons, our canChange function (and updated changeViewDate) could be something like this:
And it surely works. This is getting interesting… but I’m knackered, and there is still lots to do. But, we’ll keep it easy. Let’s take a break and I shall see you in part two.