For today's demo, I decided to try something I've been meaning to make time for - integrating with the calendar on the mobile device. Luckily there is a great plugin for this - Calendar-PhoneGap-Plugin. This plugin provides all types of hooks into the local calendar including the ability to search and add events. With that plugin in place, I whipped up a quick demo.
I began by building an application that simply returned events from a list and displayed them as is. Here is the view:
Let's look at the code behind this. First, the HTML. Since this application is so simple, I'm not using routes and templates.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">
<title></title>
<link href="lib/ionic/css/ionic.css" rel="stylesheet">
<link href="css/style.css" rel="stylesheet">
<!-- IF using Sass (run gulp sass first), then uncomment below and remove the CSS includes above
<link href="css/ionic.app.css" rel="stylesheet">
-->
<!-- ionic/angularjs js -->
<script src="lib/ionic/js/ionic.bundle.js"></script>
<!-- cordova script (this will be a 404 during development) -->
<script src="cordova.js"></script>
<!-- your app's js -->
<script src="js/app.js"></script>
<script src="js/controllers.js"></script>
<script src="js/services.js"></script>
</head>
<body ng-app="starter">
<ion-pane ng-controller="MainCtrl">
<ion-header-bar class="bar-stable">
<h1 class="title">Ionic Calendar Demo</h1>
</ion-header-bar>
<ion-content>
<div class="card" ng-repeat="event in events">
<div class="item item-divider">
{{event.title}}
</div>
<div class="item item-text-wrap">
{{ event.description }}
<p/>
<strong>When: {{ event.date | date:'short' }}</strong>
</div>
</div>
</ion-content>
</ion-pane>
</body>
</html>
This should all be fairly boiler plate. I simply loop over the events and create a card UI for each. Now let's look into the controller code.
angular.module('starter.controllers', [])
.controller('MainCtrl', function($scope, Events) {
Events.get().then(function(events) {
console.log("events", events);
$scope.events = events;
});
});
Yep, just call the service and render the events. Trivial. Now let's look at the service.
angular.module('starter.services', [])
.factory('Events', function($q) {
var incrementDate = function (date, amount) {
var tmpDate = new Date(date);
tmpDate.setDate(tmpDate.getDate() + amount)
return tmpDate;
};
//create fake events, but make it dynamic so they are in the next week
var fakeEvents = [];
fakeEvents.push(
{
"title":"Meetup on Ionic",
"description":"We'll talk about beer, not Ionic.",
"date":incrementDate(new Date(), 1)
}
);
fakeEvents.push(
{
"title":"Meetup on Beer",
"description":"We'll talk about Ionic, not Beer.",
"date":incrementDate(new Date(), 2)
}
);
fakeEvents.push(
{
"title":"Ray's Birthday Bash",
"description":"Celebrate the awesomeness of Ray",
"date":incrementDate(new Date(), 4)
}
);
fakeEvents.push(
{
"title":"Code Review",
"description":"Let's tear apart Ray's code.",
"date":incrementDate(new Date(), 5)
}
);
var getEvents = function() {
var deferred = $q.defer();
deferred.resolve(fakeEvents);
return deferred.promise;
}
return {
get:getEvents
};
});
Ok, so this is a bit more complex. I've got a set of fake data that creates four events in the future. The service then returns those fake events. Ok, so let's kick it up a notch. Given that our Calendar plugin can check for events, I'm going to update my code to display if an event has been added to the calendar or not. Here is an example.
In this screen shot, you can see buttons to add the event to your calendar. Notice that the third event though is recognized as being in the calendar. To make this work, I updated the service call for events to handle checking the calendar. It was a bit complex since each call is asynch, but $q makes it easy to handle that.
var getEvents = function() {
var deferred = $q.defer();
/*
Logic is:
For each, see if it exists an event.
*/
var promises = [];
fakeEvents.forEach(function(ev) {
//add enddate as 1 hour plus
ev.enddate = incrementHour(ev.date, 1);
console.log('try to find '+JSON.stringify(ev));
promises.push($cordovaCalendar.findEvent({
title:ev.title,
startDate:ev.date
}));
});
$q.all(promises).then(function(results) {
console.log("in the all done");
//should be the same len as events
for(var i=0;i<results.length;i++) {
fakeEvents[i].status = results[i].length === 1;
}
deferred.resolve(fakeEvents);
});
return deferred.promise;
}
I set a status value on events to represent whether or not the event exists. Back on the display side, I handle this like so:
<p ng-if="event.status">This event is added to your calendar already!</p>
<button ng-if="!event.status" ng-click="addEvent(event,$index)" class="button button-block button-balanced">Add to Calendar</button>
Fairly simple, right? Now let's look at the add code. I'll skip the controller code as all it does is call the service and update the scope.
var addEvent = function(event) {
var deferred = $q.defer();
$cordovaCalendar.createEvent({
title: event.title,
notes: event.description,
startDate: event.date,
endDate:event.enddate
}).then(function (result) {
console.log('success');console.dir(result);
deferred.resolve(1);
}, function (err) {
console.log('error');console.dir(err);
deferred.resolve(0);
});
return deferred.promise;
}
And just to prove it works - here is the event I just added:
I've put the full source code for this demo up on GitHub: https://github.com/cfjedimaster/Cordova-Examples/tree/master/calendarionic. I want to give big thanks to Eddy Verbruggen for helping me use his plugin, and for fixing a bug I encountered!