Previous post https://blogs.sap.com/2017/10/31/using-a-third-party-library-with-sapui5-application-sap-cloud-development-scenario-part-3/

Prerequisite :

Download the last Fullcalendar version from https://fullcalendar.io/download/

In my project i used FullCalendar v3.4.0 version

Download the zip file and save it on your local machine. Back to your WEB IDE Project to import files below

SAPUI5 application :

I will not comment each part of my source code because i’m not giving the unique solution. My goal is to show steps needed to build a complete Cloud project.

Create new Folder “lib” under webapp folder and import FullCalendar files

i18n content :

#~~~ Global ~~~~~~~~~~~~~~~~~~~~~~~~~~ title=Title appTitle = App Title appDescription=App Description #~~~ Event View ~~~~~~~~~~~~~~~~~~~~~~~~~~ eventTitle=SAP EBC France - Calendar Planning unknownError=Unknown Error! #~~~ Request View ~~~~~~~~~~~~~~~~~~~~~~~~~~ requestTitle=SAP EBC France - Booking Request formRequestTitle=Booking Request Detail : hostText=Host emailcustomer=Email lastnamecustomer=Last Name firstnamecustomer=First Name costcentercustomer=Cost Center emailrep=Email lastnamerep=Last Name firstnamerep=First Name costcenterrep=Cost Center evetData=Event data : requesterText=Requester evetIDText=Event ID evetTitleText=Title descriptionText=Description saveText=Send Booking Request cancelText=Cancel

Index.html content :

<!DOCTYPE HTML> <html> <head> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta charset="UTF-8"> <title>BookingEBC</title> <script id="sap-ui-bootstrap" src="../../resources/sap-ui-core.js" data-sap-ui-libs="sap.m" data-sap-ui-theme="sap_belize" data-sap-ui-compatVersion="edge" data-sap-ui-preload="async" data-sap-ui-resourceroots='{"BookingEBC": ""}'> </script> <!-- Add FullCalendar API & Script --> <link href='./lib/fullcalendar.css' rel='stylesheet' /> <script src='./lib/moment.min.js'></script> <script src='./lib/fullcalendar.js'></script> <link type="text/css" href="css/style.css"> <script> sap.ui.getCore().attachInit(function() { new sap.m.Shell({ app: new sap.ui.core.ComponentContainer({ height : "100%", name : "BookingEBC" }) }).placeAt("content"); }); </script> <style> body { width: 800px; margin: 40px 10px; padding: 10; font-family: "Lucida Grande",Helvetica,Arial,Verdana,sans-serif; font-size: 14px; } #calendar { style="width:60%" margin: 0 auto; } </style> </head> <body class="sapUiBody" id="content"> </body> </html>

View folder content :

V_ROOT.view.xml

<mvc:View controllerName="BookingEBC.controller.V_ROOT" xmlns:html="http://www.w3.org/1999/xhtml" xmlns:mvc="sap.ui.core.mvc" displayBlock="true" xmlns="sap.m"> <App id="V_Root"> <pages> <Page title="ROOT"> <content></content> </Page> </pages> </App> </mvc:View>

V_MAIN.view.xml

<mvc:View xmlns:core="sap.ui.core" xmlns:mvc="sap.ui.core.mvc" xmlns="sap.m" xmlns:html="http://www.w3.org/1999/xhtml" controllerName="BookingEBC.controller.V_MAIN"> <App class="sapUiResponsiveMargin" width="auto"> <pages> <Page title="{i18n>eventTitle}"> <content><BusyDialog id="BusyDialog" /></content> </Page> </pages> </App> </mvc:View>

V_EVENT.view.xml

The integration of Fullcalendar component is done in this view. I added an div tag in the html content, to identify my div i used calendar as an id see also the official documentation here https://fullcalendar.io/docs/usage/

<mvc:View xmlns:core="sap.ui.core" xmlns:mvc="sap.ui.core.mvc" xmlns="sap.m" xmlns:html="http://www.w3.org/1999/xhtml" controllerName="BookingEBC.controller.V_EVENT"> <App class="sapUiResponsiveMargin" width="auto"> <pages> <Page title="{i18n>eventTitle}"> <content> <Label text="{userapi>/name}" visible="false" /> <html:div id="calendar"></html:div> </content> </Page> </pages> </App> </mvc:View>

V_REQUEST.view.xml

<mvc:View xmlns:core="sap.ui.core" xmlns:mvc="sap.ui.core.mvc" xmlns="sap.m" xmlns:html="http://www.w3.org/1999/xhtml" xmlns:l="sap.ui.layout" xmlns:f="sap.ui.layout.form" controllerName="BookingEBC.controller.V_REQUEST"> <App class="sapUiResponsiveMargin" width="auto"> <pages> <Page title="{i18n>requestTitle}" showNavButton="true" navButtonPress="onNavBack"> <content> <f:Form id="FormChange354wideDual1" editable="true" > <f:title> <core:Title text="{i18n>hostText}"/> </f:title> <!-- <f:layout> <f:ResponsiveGridLayout labelSpanXL="4" labelSpanL="3" labelSpanM="4" labelSpanS="12" adjustLabelSpan="false" emptySpanXL="0" emptySpanL="4" emptySpanM="0" emptySpanS="0" columnsXL="2" columnsL="1" columnsM="1" singleContainerFullSize="false"/> </f:layout>--> <f:layout> <f:ResponsiveGridLayout labelSpanXL="5" labelSpanL="2" labelSpanM="5" labelSpanS="5" adjustLabelSpan="true" emptySpanXL="0" emptySpanL="0" emptySpanM="0" emptySpanS="0" columnsXL="1" columnsL="1" columnsM="1" singleContainerFullSize="true"/> </f:layout> <f:formContainers> <f:FormContainer> <f:formElements> <f:FormElement label=" "> <f:fields> <Switch id="switchId" state="{globalData>/enableState}" change="onSwitch"> <layoutData> <FlexItemData growFactor="1"/> </layoutData> </Switch> </f:fields> </f:FormElement> <f:FormElement label="{i18n>emailcustomer}"> <f:fields> <Input value=" " id="emailCustomer" enabled="{globalData>/enableState}" required="{globalData>/enableState}"/> </f:fields> </f:FormElement> <f:FormElement label="{i18n>lastnamecustomer}"> <f:fields> <Input value=" " id="lastnameCustomer" enabled="{globalData>/enableState}" required="{globalData>/enableState}"/> </f:fields> </f:FormElement> <f:FormElement label="{i18n>firstnamecustomer}"> <f:fields> <Input value=" " id="firstnameCustomer" enabled="{globalData>/enableState}" required="{globalData>/enableState}"/> </f:fields> </f:FormElement> <f:FormElement label="{i18n>costcentercustomer}"> <f:fields> <Input id="costcenterCustomer" value="{CUST_COSTCENTER}" required="true" showValueHelp="true" valueHelpOnly="false" valueHelpRequest="onValueHelpRequestCustomer"/> </f:fields> </f:FormElement> </f:formElements> </f:FormContainer> </f:formContainers> </f:Form> <f:Form id="FormChange354wideDual2" editable="true" > <f:title> <core:Title text="{i18n>requesterText}"/> </f:title> <!-- <f:layout> <f:ResponsiveGridLayout labelSpanXL="4" labelSpanL="3" labelSpanM="4" labelSpanS="12" adjustLabelSpan="false" emptySpanXL="0" emptySpanL="4" emptySpanM="0" emptySpanS="0" columnsXL="2" columnsL="1" columnsM="1" singleContainerFullSize="false"/> </f:layout>--> <f:layout> <f:ResponsiveGridLayout labelSpanXL="5" labelSpanL="2" labelSpanM="5" labelSpanS="5" adjustLabelSpan="true" emptySpanXL="0" emptySpanL="0" emptySpanM="0" emptySpanS="0" columnsXL="1" columnsL="1" columnsM="1" singleContainerFullSize="true"/> </f:layout> <f:formContainers> <f:FormContainer> <f:FormElement label="" visible="false"> <f:fields> <Input value="{userapi>/name}" id="nameRep" enabled="false"/> </f:fields> </f:FormElement> <f:FormElement label="{i18n>emailrep}"> <f:fields> <Input value="{userapi>/email}" id="emailRep" enabled="false"/> </f:fields> </f:FormElement> <f:FormElement label="{i18n>lastnamerep}"> <f:fields> <Input value="{userapi>/lastName}" id="lastnameRep" enabled="false"/> </f:fields> </f:FormElement> <f:FormElement label="{i18n>firstnamerep}"> <f:fields> <Input value="{userapi>/firstName}" id="firstnameRep" enabled="false"/> </f:fields> </f:FormElement> <f:FormElement label="{i18n>costcenterrep}"> <f:fields> <Input id="costcenterRep" value="{SREP_COSTCENTER}" required="true" showValueHelp="true" valueHelpOnly="false" valueHelpRequest="onValueHelpRequestSalesRep"/> </f:fields> </f:FormElement> <f:formElements> <f:FormElement label="{i18n>evetIDText}" visible="false"> <f:fields> <Input value="{ID}" id="eventID"/> </f:fields> </f:FormElement> <f:FormElement label="{i18n>evetTitleText}"> <f:fields> <Input value="{TITLE}" id="titleID" maxLength="50"/> </f:fields> </f:FormElement> <f:FormElement label="{i18n>startDateText}" visible="false"> <f:fields> <Input value="{START_DATE}" id="startDateID"/> </f:fields> </f:FormElement> <f:FormElement label="{i18n>endDateText}" visible="false"> <f:fields> <Input value="{END_DATE}" id="endDateID"/> </f:fields> </f:FormElement> <f:FormElement label="{i18n>colorText}" visible="false"> <f:fields> <Input value="{COLOR}" id="colorID"/> </f:fields> </f:FormElement> <f:FormElement label="{i18n>statusText}" visible="false"> <f:fields> <Input value="{STATUS}" id="statusID"/> </f:fields> </f:FormElement> <f:FormElement label="{i18n>descriptionText}"> <f:fields> <TextArea value="{DESCRIPTION}" id="descriptionID" rows="8"/> </f:fields> </f:FormElement> <f:FormElement label="{i18n>yearText}" visible="false"> <f:fields> <Input value="{YEAR}" id="yearID"/> </f:fields> </f:FormElement> <f:FormElement label="{i18n>monthText}" visible="false"> <f:fields> <Input value="{MONTH}" id="monthID"/> </f:fields> </f:FormElement> <f:FormElement label="{i18n>dayText}" visible="false"> <f:fields> <Input value="{DAY}" id="dayID"/> </f:fields> </f:FormElement> </f:formElements> </f:FormContainer> </f:formContainers> </f:Form> </content> <footer> <Bar> <contentRight> <Button id="save" text="{i18n>saveText}" type="Emphasized" press="onSave"/> <Button id="cancel" text="{i18n>cancelText}" press="onNavBack"/> </contentRight> </Bar> </footer> </Page> </pages> </App> </mvc:View>

Controller folder content :

V_ROOT.controller.js 

sap.ui.define([ "sap/ui/core/mvc/Controller" ], function(Controller) { "use strict"; return Controller.extend("BookingEBC.controller.V_ROOT", { }); });

V_MAIN.controller.js

sap.ui.define([ "sap/ui/core/mvc/Controller" ], function(Controller) { "use strict"; return Controller.extend("BookingEBC.controller.V_MAIN", { //Initial Load onInit: function() { }, // After Loading UI5 component onAfterRendering: function() { var oDialog = this.getView().byId("BusyDialog"); oDialog.open(); jQuery.sap.delayedCall(2000, this, function () { oDialog.close(); }); this.goToEventCalendar(); }, goToEventCalendar: function() { var date = new Date(); var currentDay = date.getFullYear() + "-" + ("0" + (date.getMonth() + 1)).slice(-2) + "-" + ("0" + date.getDate()).slice(-2); // Now Get the Router Info var oRouter = sap.ui.core.UIComponent.getRouterFor(this); // Tell the Router to Navigate To Route_Event which is linked to V_EvenT view oRouter.navTo("Route_Event", {SelectedDate: currentDay}); } }); });

V_EVENT.controller.js

This is the main point to integrate Fullcalndar component so i will explain how we can do it.

OnInit() function validate the Rout config and call the _onRouterFound. See

https://sapui5.hana.ondemand.com/1.36.9/docs/guide/e5200ee755f344c8aef8efcbab3308fb.html for more information about routing and navigation

_onRouterFound() read the navigation route arguments and call _readBackEndEvents()

Note : I’m using a date as a parameter in my Route Pattern

_readBackEndEvents() call the Events Entity from our Odata service

_mapResults() Read data from Events Entity and save it to a local array variable “events”.  To manage and use the Fullcalendar component we have this instruction :

this.byId("calendar").$().fullCalendar({});
 //MAP JSON Resut To Model _mapResults: function(data, SelectedDate) { var events = []; var self = this; for (var i = 0; i < data.results.length; i++) { events.push({ id: data.results[i].ID, title: data.results[i].TITLE, start: data.results[i].START_DATE, end: data.results[i].END_DATE, status: data.results[i].STATUS, color: data.results[i].COLOR }); } this.byId("calendar").$().fullCalendar( { // Alow click eventClick: function(calEvent, jsEvent, view) { self.goToRequestForm(calEvent); }, // put your options and callbacks here header: { left: 'prev,next today', center: 'title', right: 'month,agendaWeek' }, height: 700, weekends: false, minTime: "09:00:00", maxTime: "20:0:00", defaultDate: SelectedDate, //'2017-05-12', defaultView: 'agendaWeek', navLinks: true, // can click day/week names to navigate views editable: false, eventLimit: true, // allow "more" link when too many events //events: events, allDay: false }); this.byId("calendar").$().fullCalendar('removeEvents'); this.byId("calendar").$().fullCalendar( 'addEventSource', events); this.byId("calendar").$().fullCalendar( 'refetchEvents' ); },

Complete source code :

sap.ui.define([ "sap/ui/core/mvc/Controller" ], function(Controller) { "use strict"; return Controller.extend("BookingEBC.controller.V_EVENT", { //Initial Load onInit: function() { // Call UserAPI and stored into a JSON Model var userModel = new sap.ui.model.json.JSONModel("/services/userapi/currentUser"); this.getView().setModel(userModel, "userapi"); // Get the Router Info var oRouter = sap.ui.core.UIComponent.getRouterFor(this); // Validate/Match the Router Details sent from source using oRouter.navTo("Route_Event", {SelectedDate: selectedDate}); oRouter.getRoute("Route_Event").attachMatched(this._onRouteFound, this); }, _onRouteFound: function(oEvt) { var oArgument = oEvt.getParameter("arguments"); this._readBackEndEvents(oArgument.SelectedDate); }, _readBackEndEvents: function(SelectedDate) { var oODataModel = sap.ui.getCore().getModel(); var self = this; oODataModel.read("/Events", { method: "GET", success: function(data, oResponse) { self._mapResults(data, SelectedDate); }, error: function() { // show error messge sap.m.MessageToast.show("unknownError"); } }); }, // After Loading UI5 component onAfterRendering: function() { //this._readBackEndEvents(); /* var oODataModel = sap.ui.getCore().getModel(); var self = this; oODataModel.read("/Event", { method: "GET", success: function(data, oResponse) { self._mapResults(data); }, error: function() { // show error messge sap.m.MessageToast.show("unknownError"); } }); */ }, //MAP JSON Resut To Model _mapResults: function(data, SelectedDate) { var events = []; var self = this; for (var i = 0; i < data.results.length; i++) { events.push({ id: data.results[i].ID, title: data.results[i].TITLE, start: data.results[i].START_DATE, end: data.results[i].END_DATE, status: data.results[i].STATUS, color: data.results[i].COLOR }); } this.byId("calendar").$().fullCalendar( { // Alow click eventClick: function(calEvent, jsEvent, view) { self.goToRequestForm(calEvent); }, // put your options and callbacks here header: { left: 'prev,next today', center: 'title', right: 'month,agendaWeek' }, height: 700, weekends: false, minTime: "09:00:00", maxTime: "20:0:00", defaultDate: SelectedDate, //'2017-05-12', defaultView: 'agendaWeek', navLinks: true, // can click day/week names to navigate views editable: false, eventLimit: true, // allow "more" link when too many events //events: events, allDay: false }); this.byId("calendar").$().fullCalendar('removeEvents'); this.byId("calendar").$().fullCalendar( 'addEventSource', events); this.byId("calendar").$().fullCalendar( 'refetchEvents' ); }, goToRequestForm: function(calEvent) { //The requester can click only on a Green Slot if (calEvent.status !== "F") { return; } // Get Property of the Clicked Item. i.e. Event.id of the item which was clicked var selectEventID = calEvent.id; //calEvent.getSource().getBindingContext().getProperty("id"); // Now Get the Router Info var oRouter = sap.ui.core.UIComponent.getRouterFor(this); // Tell the Router to Navigate To Route_Request which is linked to V_REQUEST view oRouter.navTo("Route_Request", {SelectedItem: selectEventID}); } }); });

V_EVENT.controller.js

sap.ui.define([ "sap/ui/core/mvc/Controller" ], function(Controller) { "use strict"; return Controller.extend("BookingEBC.controller.V_EVENT", { //Initial Load onInit: function() { // Call UserAPI and stored into a JSON Model var userModel = new sap.ui.model.json.JSONModel("/services/userapi/currentUser"); this.getView().setModel(userModel, "userapi"); // Get the Router Info var oRouter = sap.ui.core.UIComponent.getRouterFor(this); // Validate/Match the Router Details sent from source using oRouter.navTo("Route_Event", {SelectedDate: selectedDate}); oRouter.getRoute("Route_Event").attachMatched(this._onRouteFound, this); }, _onRouteFound: function(oEvt) { var oArgument = oEvt.getParameter("arguments"); this._readBackEndEvents(oArgument.SelectedDate); }, _readBackEndEvents: function(SelectedDate) { var oODataModel = sap.ui.getCore().getModel(); var self = this; oODataModel.read("/Events", { method: "GET", success: function(data, oResponse) { //self.byId("calendar").$().fullCalendar('removeEvents'); self._mapResults(data, SelectedDate); }, error: function() { // show error messge sap.m.MessageToast.show("unknownError"); } }); }, // After Loading UI5 component onAfterRendering: function() { //this._readBackEndEvents(); /* var oODataModel = sap.ui.getCore().getModel(); var self = this; oODataModel.read("/Event", { method: "GET", success: function(data, oResponse) { self._mapResults(data); }, error: function() { // show error messge sap.m.MessageToast.show("unknownError"); } }); */ }, //MAP JSON Resut To Model _mapResults: function(data, SelectedDate) { //sap.ui.commons.MessageBox.alert("Current Month" + SelectedDate); var events = []; var self = this; for (var i = 0; i < data.results.length; i++) { events.push({ id: data.results[i].ID, title: data.results[i].TITLE, start: data.results[i].START_DATE, end: data.results[i].END_DATE, status: data.results[i].STATUS, color: data.results[i].COLOR }); } this.byId("calendar").$().fullCalendar( { // Good method to change rendering /* eventAfterRender: function (event, element, view) { if (event.status === "F") { element.css('background-color', '#77DD77'); } else if (event.status === "W") { element.css('background-color', '#FFFF00'); } else if (event.status === "B") { element.css('background-color', '#FF0000'); } },*/ // Alow click eventClick: function(calEvent, jsEvent, view) { self.goToRequestForm(calEvent); }, // Catch navigation click on calendar object /* viewRender: function (view, element) { var b = view.start._d; var m = b.getMonth(); sap.ui.commons.MessageBox.alert("Current Month" + m); },*/ // put your options and callbacks here header: { left: 'prev,next today', center: 'title', right: 'month,agendaWeek' }, height: 700, weekends: false, minTime: "09:00:00", maxTime: "20:0:00", defaultDate: SelectedDate, //'2017-05-12', defaultView: 'agendaWeek', navLinks: true, // can click day/week names to navigate views editable: false, eventLimit: true, // allow "more" link when too many events //events: events, allDay: false }); this.byId("calendar").$().fullCalendar('removeEvents'); this.byId("calendar").$().fullCalendar( 'addEventSource', events); this.byId("calendar").$().fullCalendar( 'refetchEvents' ); }, goToRequestForm: function(calEvent) { //The requester can click only on a Green Slot if (calEvent.status !== "F") { return; } // Get Property of the Clicked Item. i.e. Event.id of the item which was clicked var selectEventID = calEvent.id; //calEvent.getSource().getBindingContext().getProperty("id"); // Now Get the Router Info var oRouter = sap.ui.core.UIComponent.getRouterFor(this); // Tell the Router to Navigate To Route_Request which is linked to V_REQUEST view oRouter.navTo("Route_Request", {SelectedItem: selectEventID}); } }); });

Conclusion :

This is the last part of my series i hope this content will help you to understand what we need to complete an End-To-End SAP development Cloud scenario. Below a list of skills needed :

  1. Front End/UI Part ( JavaScript, HTML, CSS, SAPUI5 ), WEB IDE utilisation
  2. SCP Cloud Platform ( set up, connectivity )
  3. Odata Knowledge and how to consume our service in the Front-End app
  4. Back-End Development ( it will depend of your technologie ) in my case i used a HANA MDC so XSJS and SQL Skills, table creation, some admin tasks ( creating users/roles )

 

New NetWeaver Information at SAP.com

Very Helpfull

User Rating: Be the first one !