Technology Blogs by SAP
Learn how to extend and personalize SAP applications. Follow the SAP technology blog for insights into SAP BTP, ABAP, SAP Analytics Cloud, SAP HANA, and more.
cancel
Showing results for 
Search instead for 
Did you mean: 
former_member208486
Participant
Happy #APIFriday everyone! Sorry for the hiatus, but I am excited to be back. I got a lot of interest in showcasing apps from my UI5con talk and I promise I will respond to you who tweeted at me soon. It's been a little crazy in developer relations world.

Today we are going to be looking at the Google Calendar API for our UI5 app. Isn't it so convenient that on our phones we can merge calendars from different people and hosts? I have my Outlook, Google, and local calendars all display on my Calendar app on my phone. Now I haven't spent a lot of time trying to add my Outlook to my Google calendar or vice versa on computer, but I know they don't sync as easily as they do on our phones. Wouldn't it be nice to have all your calendars in one place on your computer? And be able to include your co-worker's, friend's, or family's calendar as well? As part of an ongoing series, I am going to be building a syncing app for different types of calendars in UI5. Let's start with Google Calendar!



To get started, you will need a Google account and to have created credentials in Google Developers' API Manager. This will provide you with a Client Secret and Client ID, which are needed for OAuth 2.0, my favorite auth protocol <3. Per usual, I had a hard time getting my OAuth exchange to work (I will figure this out TBD or if someone already has, feel free to enlighten us!). After some googling about Google, I found this OAuth playground provided by Google which made getting the OAuth token much easier. If you are having problems too, check out the OAuth Playground for Google Developers. These tokens are only good for 1 hour, so they are not meant as a productive solution. However, for quick development and testing, it works just fine.



The documentation for the Google Calendar API can be found here. We are going to use the CalendarList > list endpoint to get our calendar ID and the Events > list endpoint to get the events from our calendar.

To get the events from a calendar, we need to get the calendar ID. The list of YOUR calendars can be founds using CalendarList endpoint.



This will return a list of calendars for your user, hence the "me" parameter. Grab the id value from the return of the List CalendarList method.



The Google Developer OAuth Playground also lets you test APIs available from Google, so this is a good place to bookmark if you do a lot of Google development.

Once you have your calendar ID, you can go ahead and try the List Events API in the Playground before we move it into your UI5 app. This way we can get familiar with the structure returned and see how we enter the necessary parameters. For the calendar demo I showed, I limited the returned list of events to the day selected on the calendar. To do this, we need to use the timeMin and timeMax parameters in the Events list endpoint.
https://www.googleapis.com/calendar/v3/calendars/{your_calendar_id}/events

https://www.googleapis.com/calendar/v3/calendars/{your_calendar_id}/events?timeMin=xxx&timeMax=xxx

Now that we have figured out how the Calendar API works and have a better understanding of the data we'll be getting, let's move over to our SAP Web IDE.

To get started, I used the sample Multiple Month Calendar app available on the SAPUI5 Explored page. I made a couple tweaks to make my layout look the way it does. I changes "months" from 2 to 1 and removed the Select Today button. I also replaced the <Label> and <Text> controls with an <ObjectHeader> to make the selected date stand out more. My view looks like this to start with between the content tags. Don't forget to add the unified namespace.
<content>
<l:HorizontalLayout>
<u:Calendar
id="calendar"
months="1"
select="handleCalendarSelect" />
<l:VerticalLayout>
<ObjectHeader
id="selectedDate"
intro="Selected Date (yyyy-mm-dd)"
title="No Selected Date" />
</l:VerticalLayout>
</l:HorizontalLayout>
</content>

Don't forget to add the unified namespace in the view.
xmlns:u="sap.ui.unified"

Since we replaced the <Text> control with an <ObjectHeader>, there is a change that needs to be made in the controller. In your controller, change .setText() to .setTitle().
oText.setTitle(this.oFormatYyyymmdd.format(oDate));

You can also remove the handleSelectToday function as we will not be needing it since we deleted the Select Today button.

In your view file, let's add a table to display our events for the selected day. The table should have 2 columns, one called Events and one called Time. The cells for the columns are an ObjectHeader control and a Text control.
<Table id="idEvents">
<columns>
<Column>
<Text text="Event" />
</Column>
<Column>
<Text text="Time (if applicable)" />
</Column>
</columns>
<items>
<ColumnListItem>
<cells>
<ObjectIdentifier
title="Text"
text="Location"/>
<Text
text="Time" />
</cells>
</ColumnListItem>
</items>
</Table>

In the controller file, we need to create a model to bind our data from Google Calendars to so we can display it in the Table. In the onInit function, set a new model called "events" to the view. Don't forget to define the JSONModel in the controller.

Define:
"sap/ui/model/json/JSONModel"

onInit:
//model to hold the data for the view
var eventsData = new JSONModel({"data": {}});
this.getView().setModel(eventsData, "events");

We create an empty JSONModel to bind to the view so we have control over the structure of the data.



In the view file, we can now set the items property of the table to add the aggregate for the <items> definition.
<Table id="idEvents"
items="{events>/data}">

Back to the controller!

In the _updateText function, we are going to add the call to the Google Calendar API. In a lot of my blogs, I have used a jQuery AJAX request which is just another way of doing a XMLHttpRequest. In this blog, we're going to use the XHR (XMLHttpRequest) calling convention so we are familiar with both ways. To start, we have to create a new XMLHttpRequest variable.
var xhr = new XMLHttpRequest();

We want to attach a listener to the request, so when the request returns a response we can take the appropriate action. This is an asynchronous call.
xhr.addEventListener("readystatechange", function () {
if (this.readyState === this.DONE) {

}
});

To prepare the call, we have to open the request and set the method, set the request headers, and finally send the request.
//setting request method
xhr.open("GET", "https://www.googleapis.com/calendar/v3/calendars/{your_calendar_id}/events");

//adding request headers
xhr.setRequestHeader("Content-Type", "application/json");
xhr.setRequestHeader("Accept", "application/json");
xhr.setRequestHeader("Authorization", "Bearer "+ token);

//sending request
xhr.send(data);

Don't forget to use your Calendar ID and your OAuth Token. You can refresh your token in step 2 of the OAuth Playground.

Now once we get data back, we want to bind it to our model so we can use it in the view. To do this, we need to capture the scope of this outside of the XHR request callback. You can set this to a self variable. We will need the view scope of this in the callback function. (The callback function is the event listener.)
var self = this;

In the event lister, when the ready state is done, we can then bind the data returned to our model used in our view. To do this, we need to parse the JSON returned and then bind it to the events model we created in the onInit function. We will set the data property to be the items array returned from the Google Calendar API.
var jsonResults = JSON.parse(this.response).items;
self.getView().getModel("events").setProperty("/data", jsonResults);

However, this is returning us all the events from the selected calendar. We need to add parameters to the call to limit the events to only the selected day. Please note that at present I am having some trouble with date math so please excuse the wonky (and hacky) date math.

We can grab the date selected from the from the oDate used to populate the text (or ObjectHeader) control. This will be our lower bound. To get the upper bound (tomorrow at midnight) is a little tricky because dateTimes have a timezone. If you have worked with dateTimes before, how many times have you forgotten to consider this detail?

So here's the wonky date math I came up with. Can you do better? (probably you can) Add these variable definitions before opening the request.
var today = this.oFormatYyyymmdd.format(oDate);
var tomorrow = new Date(today);
tomorrow.setUTCDate(tomorrow.getDate() + 3);
tomorrow = this.oFormatYyyymmdd.format(tomorrow);

Now the Google Calendar API takes a datetime as as a RFC3339 timestamp so we can actually use our date formatter.

In the xhr.open() function, we need to add the timeMin and timeMax parameters to the URL. Before the closing braket, add the following parameters to the request.
+
"?"+
"timeMin=" + today + "T00:00:00-00:00" +
"&timeMax=" + tomorrow + "T00:00:00-00:00"

This tells our request we have parameters coming and we passed them in the RFC3339 timestamp format. Our min time is Today at midnight. The timeMin parameter is inclusive. The max time is set to tomorrow at midnight. The timeMax parameter is exclusive.

Here's what my controller looks like for the _updateText function.



The last thing we want to do for now is to add the data to the table and format the time for the Time column.

In your view, go to your table items. We will change the hard coded text to values from our events model. In the <ObjectIdentifier> we can change title to {events>summary} and text to {events>location}.
<ObjectIdentifier				
title="{events>summary}"
text="Location: {events>location}"/>

In the <Text> cell, we can change the text to {events>start/dateTime}.
<Text 
text="{events>start/dateTime}" />

This will display the dateTime of the start of the event (when applicable, aka when it's not an all day event) as a long Date-Time string. We can add a formatter to make it more readable. To add a formatter to the field, update the text attribute binding to have a path (to the data) and a formatter (name of the formatting function).
<Text 
text="{
path: 'events>start/dateTime',
formatter: '.formatter.timeDisplay' }" />

Now we need to create a formatter function. In your model folder (or create a model folder under webapp), add a new file called formatter.js.



Open the formatter file. In the formatter, we want to define a new function and give it one function to return called timeDisplay.
sap.ui.define([
], function () {
"use strict";
return {

timeDisplay : function (sValue) {

}

};
}
);

In the timeDisplay function, we want to check to see if the value is blank. If it is not, we want to return a formatter date with only the time.
timeDisplay : function (sValue) {
if (!sValue) {
return "";
}
var date = new Date(sValue);
return date.toTimeString();
}

The last step is to set up the formatter in the controller file. Back in your controller, add the formatter file to the defines state.



Define the formatter in the return statement and SAVE ALL YOUR CHANGES. You should try to save as your go along.
formatter: formatter,



RUN your application. You should see a calendar and an empty table. As you click around, the selected data value should change to match the date you pick and if there are any events in your calendar, the information should fill your table. Awesome!



You can add more of your Google Calendars to the view and your friends if they give you access. We will continue to take a look at building out our calendar in future weeks. What do you think?

As always, thanks for following along and I hope you enjoyed it! I am always taking suggestions if you tweet at me (@mlhassett) and use the hashtag #APIFriday. Looking forward to hearing from you!
3 Comments