Making a DatePicker directive in Ionic Framework


Update – 15th September 2015

I wrote this article a little while ago about making a custom date & time picker for Ionic apps. When I tested this on WP8 devices, I found that they weren’t supported so I had to update my solution to accomodate Windows Phones too. I have updated my code below to show how I achieved this. I am using 2 different cordova plugins to keep Android & ios as up to date as possible, but really you probably only need to second plugin if you want to minimise the amount of resources used. If VitaliiBlagodir’s plugin support WP8 in the future, I would remove the Windows specific functionality and combine everything together.


Introduction

For my current project I’m using Ionic Framework to build a hybrid mobile app which runs across Android, ios and Windows phones. It’s built with Phonegap and Angular and so far I’ve found it really impressive, with great documentation and a very good community around it. It comes with a great selection of plugins and utilities to use. Part of the app I am making requires the user to enter start & end dates for a particular function, so I created a directive to do this, so that the code could easily be re-used on other parts of the app.

I’m using the ngCordova library which has some very useful plugins. One of the main reasons I’m using it is because it wraps ordinary phonegap functions in Promises, allowing the app to run asynchronously. The plugin we’ll be using for this directive is $cordovaDatePicker


Install the datepicker plugin

First, install the plugin by running the following command in your terminal:

cordova plugin add https://github.com/VitaliiBlagodir/cordova-plugin-datepicker.git

Second, install another plugin which does support Windows phones:

cordova plugin add https://github.com/michaelfranz89/cordova-plugin-datepicker

Let’s get started

Next, we’ll set up some models on our controller’s scope to store the dates in. In our controller we’ll use:


$scope.requestData = {
	start_date: new Date()
};

The HTML for the directive looks like so:


<div datetimepicker mode="'date'" datetime="requestData.start_date"></div>

In this example, you can see that we are passing in the requestData.start_date variable into the directive. This variable will be updated with the date once one has been selected using the datepicker.


Set up the Angular Directive

Finally, we need to set up the directive code. How you set up your code structure is entirely up to you, but in this example I have set up the directives inside a new Angular module called ‘appDirectives’.


var appDirectives = angular.module('appDirectives', []);

appDirectives.directive('datetimepicker', function($rootScope, $state, $ionicPopup, $cordovaDatePicker, $timeout, $translate) {
	return {
		restrict: 'A',
		replace: true,
		scope: {
			selectedDateTime: '=datetime',
			mode: '='
		},
		link: function(scope) {

			scope.selectDateTime = function() {

				if (!scope.selectedDateTime) {
					scope.selectedDateTime = new Date();
				}

				var options = {
					date: scope.selectedDateTime,
					mode: scope.mode
				};

				/***
				*
				* Make sure that the user's browser/device can use the datepicker functionality
				*
				***/

				if (navigator.userAgent.match(/(iPhone|iPod|iPad|Android)/gi)) {

					$cordovaDatePicker.show(options).then(function(date) {
						$timeout(function() {
							scope.selectedDateTime = date;
						}, 50);
					}, function(err) {
						alert(err);
					});

				} else if ( ionic.Platform.platform() === 'windowsphone') {


					// $cordova datepicker doesn't support wp8 
					// so we'll use https://github.com/michaelfranz89/cordova-plugin-datepicker for this instead

					// If we are getting date & time, we need to do them separately.
					// First of all, get the date
					if (scope.mode === 'datetime') {
						options.mode = 'date';
					}

					datePicker.show(options, function(date) {

						if (scope.mode === 'datetime') {
							// If we are getting date & time, we now need to get the time and then combine the 2 values together
							datePicker.show({
								mode: 'time',
								date: scope.selectedDateTime
							}, function(time) {
								// Create a new date with year, month and date from the date variable and hours, minutes and seconds from the time variable
								$timeout(function() {
									scope.selectedDateTime = new Date(date.getFullYear(), date.getMonth(), date.getDate(), time.getHours(), time.getMinutes(), time.getSeconds());
								}, 50);
							});
						} else {
							// If we're only getting the date OR time, send the value back
							$timeout(function() {
								scope.selectedDateTime = date;
							}, 50);
						}

					});

				} else {

					$translate(['directives.datepicker.DATEPICKER_ERROR_TITLE', 'directives.datepicker.DATEPICKER_ERROR'])
						.then(function(translations) {

							$ionicPopup.alert({
								title: translations['directives.datepicker.DATEPICKER_ERROR_TITLE'],
								template: translations['directives.datepicker.DATEPICKER_ERROR']
							});

						});

				}

			};

		},
		template: '<button type="button" class="button button-icon icon ion-ios-calendar-outline" ng-click="selectDateTime()"></button>'
	}
});


As you can see in the code above, we define a template for the directive which replaces the HTML we set earlier. This is an Ionic icon button, using the calendar icon. We also set up an ng-click function on the button so that we can start the datePicker functionality once the user clicks the button.

Once the user clicks the button, the selectDateTime() function is called. If the date value in the controller is null, we set the selectedDate using new Date(). This makes sure that the $cordovaDatePicker plugin has a date to start from. The plugin isn’t supported in desktop browsers, so I am using $ionicPopup to display an error message if the user is not on a mobile device. If the user is on a mobile device, we call the show() function on $cordovaDatePicker, which presents the user with the device’s native date picker. The ‘then’ promise after the show() function takes the date received back from the native datepicker, and then we can update the value in our earlier defined controller.


Different Modes

You may have noticed earlier that we included a ‘mode’ attribute in our HTML, which was set to ‘date’. You can also set this field to either ‘time’ or ‘datetime’. With previous versions only iPhone would allow date & time picking together, but the plugin has recently been updated to allow this on Android devices too which is very useful when you require a specific time to be entered by the user. iPhone has it’s own date & time picker where you can set both values at the same time, but when using an Android device, the user will see the date selector first, and then once they have selected that, the time picker will be displayed. A great addition by the plugin’s author.


Windows Phone Support

The second plugin we installed supports WP8, so inside the directive we can check what platform the app is being ran on, using ionic.Platform.platform() and then running different code. To take both the date and time, I have made it so the date is taken first, then we run the picker again but with the mode set to time. We then combine to two values together, taking the Year, Month and Date from the ‘date’ variable, and the Hours, Minutes and Seconds from the ‘time’ variable.

Oddly, I found that when using Ionic’s `ionic.Platform.isWindowsPhone()`, it didn’t return true for my device so I used the platform() function instead to check manually.


Leave a Reply

Your email address will not be published. Required fields are marked *