Hi All,

        In this blog i am trying to share my attempt in creating a Custom UI5 Application following a Fiori application design guidelines.As shown in a previous article of mine(Creating A Fiori UI5 Application Using Fragments ).I will explain how i created a Flight booking application template application.On my next blog i will integrate the same application with Odata to make it a fully working UI5 Fiori application.I am using fragment concept in my below application which is already explained in the provided link above.

1)Creating a new UI5 mobile application in eclipse.

Capture1.PNG

Here i am going to make a sap mobile UI5 application.So i will be selecting the sap.m library for my project

Capture2.PNG

Give the name of the main view of your application and select the Development Paradigm(I have selected XML view because all the apps in Fiori Wave 2 has been developed on XML views,even the wave 1 application codes are ported to XML coding)Capture3.PNG

2)Create the necessary views for the fiori application.Here i am making a Split App design which makes use of the “sap.m.SplitApp” control in UI5.This app basically has a Master View and a Detail view.

  In addition to the two views mentioned above,There will be two more additional files which needs to be created

  i)Component.js – In a fiori based application instead of “index.html” fiori application always invokes the Component.js by default,this is a design standard for       every fiori applications.

  ii)App.view.js – this js file will create the required Split App control on load of the application

Capture4.PNG

Entire Hierarchy of my project can be seen in the below screenshot

Capture30.PNG

i) Component.js


    In component.js we will write the code to detect the device type whether its a desktop or a mobile device.This is essential to hide the Split App’s Master view by default if the screen size is small.This component.js will be extending the control sap.ui.core.UIComponent as shown in the below screenshot

Capture5.PNG

In the below screenshot you can see the code to detect the device Model. Based on whether its a phone or desktop browser,I am hiding the Master Page.

Similarly i am declaring the language support files for maintaining multiple language support for my application.More details on language support in Fiori applications can be found in this discussion (SAPUI5 and the arabic language)

I am also setting the mock.json file which will be used to bind to the Tile container to define the Types of the tiles and the text to be shown in each tile.Capture6.PNG


jQuery.sap.declare("sap.ui.demo.myFiori.Component");
jQuery.sap.require("sap.m.routing.RouteMatchedHandler");
sap.ui.core.UIComponent.extend("sap.ui.demo.myFiori.Component", {
  createContent : function() {
//alert("component");
  // create root view
  var oView = sap.ui.view({
  id : "app",
  viewName : "sap.ui.demo.myFiori.view.App",
  type : "JS",
  viewData : { component : this }
  });
  // set device model
  var deviceModel = new sap.ui.model.json.JSONModel({
  isPhone : sap.ui.Device.system.phone,
  isNoPhone : !sap.ui.Device.system.phone,
  listMode : (sap.ui.Device.system.phone) ? "None" : "SingleSelectMaster",
  listItemType : (sap.ui.Device.system.phone) ? "Active" : "Inactive"
  });
  deviceModel.setDefaultBindingMode("OneWay");
  oView.setModel(deviceModel, "device");
  //debugger
//alert("oView created");
  var langModel = new sap.ui.model.resource.ResourceModel({
  bundleUrl : "i18n/messageBundle.properties"
  });
  oView.setModel(langModel,"i18n");
  // Set the model
// var url = "proxy/http/zmetdc00.wdf.sap.corp:55080/sap/opu/odata/IWBEP/GWDEMO/";
// var oModel = new sap.ui.model.odata.ODataModel(url, true);
// oView.setModel(oModel);
  oModel = new sap.ui.model.json.JSONModel("model/mock.json");
  oView.setModel(oModel);
  //alert("oView returned");
  return oView;
  }
});










ii) App.view.js

In this Javascript file i am basically creating an app and inserting a SplitApp control into that app using the following code.

this.app = new sap.m.SplitApp().setMode(sap.m.SplitAppMode.HideMode);

Also i am specifying the property of the split app to hide the Master View by default (sap.m.SplitAppMode.HideMode).You can remove that property if you wish to keep the Master page expanded in desktop browser by default.

Then i am declaring my Master and Detail view page and adding it the app as shown in the below screenshot

Capture7.PNG


sap.ui.jsview("sap.ui.demo.myFiori.view.App", {
  getControllerName: function () {
  return "sap.ui.demo.myFiori.view.App";
  },
  createContent: function (oController) {
  //alert("app started");
  // to avoid scroll bars on desktop the root view must be set to block display
  this.setDisplayBlock(true);
  // create app
  //this.app = new sap.m.App();
  this.app = new sap.m.SplitApp().setMode(sap.m.SplitAppMode.HideMode);
  // load the master page
  var master = sap.ui.xmlview("Master", "sap.ui.demo.myFiori.view.Master");
  master.getController().nav = this.getController();
  this.app.addPage(master, true); // true means this is the master page
  // load the detail page
  var detail = sap.ui.xmlview("Detail", "sap.ui.demo.myFiori.view.Detail");
  detail.getController().nav = this.getController();
  this.app.addPage(detail, false);
  // done
  return this.app;
  }
});





App.controller.js




sap.ui.controller("sap.ui.demo.myFiori.view.App", {
  /**
  * Navigates to another page
  * @param {string} pageId The id of the next page
  * @param {sap.ui.model.Context} context The data context to be applied to the next page (optional)
  */
  to : function (pageId, context) {
  var app = this.getView().app;
  // load page on demand
  var master = ("Master" === pageId);
  //alert(master);
  if (app.getPage(pageId, master) === null) {
  var page = sap.ui.view({
  id : pageId,
  viewName : "sap.ui.demo.myFiori.view." + pageId,
  type : "XML"
  });
  //
  page.getController().nav = this;
  app.addPage(page, master);
  jQuery.sap.log.info("app controller > loaded page: " + pageId);
  }
  // show the page
  app.to(pageId);
  // set data context on the page
  if (context) {
  var page = app.getPage(pageId);
  page.setBindingContext(context);
  }
  },
  /**
  * Navigates back to a previous page
  * @param {string} pageId The id of the next page
  */
  back : function (pageId) {
  this.getView().app.backToPage(pageId);
  }
});



Now we will look into the codes of MasterView and DetailView


MasterView.xml (The  details about the line 14 in the below code is being explained in my previous article about fragments,then link to that is mentioned at the begining of this blog)


<core:View controllerName="sap.ui.demo.myFiori.view.Master"
  xmlns="sap.m" xmlns:core="sap.ui.core">
  <Page title="{i18n>MasterTitle}">
  <subHeader>
<Bar>
<contentLeft>
<SearchField
search="handleSearch"
width="100%" >
</SearchField>
</contentLeft>
</Bar>
</subHeader>
  <core:Fragment fragmentName="sap.ui.demo.myFiori.Fragments.Sidepanel" type="XML" />
  </Page>
</core:View>



Detail.View.xml


<core:View
  controllerName="sap.ui.demo.myFiori.view.Detail"
  xmlns="sap.m"
  xmlns:layout="sap.ui.layout"
  xmlns:core="sap.ui.core" >
<Page title="{i18n>DetailTitle}"
      showNavButton="{device>/isPhone}"
   navButtonPress="handleNavButtonPress">
  <subHeader>
  <Bar>
  <contentRight>
  <Button id="user" icon="sap-icon://account" press="handlePopoverPress" ></Button>
  </contentRight></Bar>
  </subHeader>
<layout:Grid defaultSpan="L12 M6 S12" hSpacing="1">
  <layout:content>
<VBox>
<core:Fragment fragmentName="sap.ui.demo.myFiori.Fragments.MainContent" type="XML" />
<HBox><Image src="Images/line.png"></Image></HBox>
  <NavContainer id="navCon2" width="60em" height="43em">
  <core:Fragment fragmentName="sap.ui.demo.myFiori.Fragments.Tiles" type="XML" />
  </NavContainer>
</VBox>
    </layout:content>
</layout:Grid>
<footer>
  <core:Fragment fragmentName="sap.ui.demo.myFiori.Fragments.Footer"
  type="XML" />
  </footer>
</Page>
</core:View>



Detail.controller.js (This controller file for the Detail.View page handles the Popup controls,Tile interactions etc as shown in the final output of this UI5 applications towards the end of this blog)


jQuery.sap.require("sap.ui.demo.myFiori.util.Formatter");
sap.ui.controller("sap.ui.demo.myFiori.view.Detail", {
  handleNavButtonPress : function (evt) {
  this.nav.back("Master");
  },
  onAlert : function (){
  alert("Alert Box");
  },
  closeButton:function(oEvent) {
  oEvent.getSource().getParent().close();
  },
  openDialog: function (Msg) {
  if (!this[Msg]) {
  this[Msg] = sap.ui.xmlfragment("sap.ui.demo.myFiori.Fragments.Popup",this );// associate controller with the fragment
  }
  //this[Msg].bindElement("/ProductCollection/0");
  this[Msg].open();
  },
  openDialog1: function (Msg) {
  if (!this[Msg]) {
  this[Msg] = sap.ui.xmlfragment(
  "sap.ui.demo.myFiori.Fragments.MsgDialog",this // associate controller with the fragment
  );
  }
// this[Msg].bindElement("/ProductCollection/0");
  this[Msg].open();
  },
  onMsgDialogPress: function (oEvent) {
  this.openDialog('Std');
  },
  onDialogCloseButton: function (oEvent) {
  oEvent.getSource().getParent().close();
  },
  onDialogokButton: function (oEvent) {
  var msg = 'Pop Up Closed';
  sap.m.MessageToast.show(msg);
  oEvent.getSource().getParent().close();
  },
  onInit : function (evt) {
     // set explored app's demo model on this sample
//    var oModel = new sap.ui.model.json.JSONModel("resource/products.json");
//    this.getView().setModel(oModel);
   },
   onExit : function () {
     if (this._oPopover) {
       this._oPopover.destroy();
     }
   },
   handlePopoverPress: function (oEvent) {
     if (! this._oPopover) {
       this._oPopover = sap.ui.xmlfragment("sap.ui.demo.myFiori.Fragments.Popover", this);
     }
     this._oPopover.setModel(this.getView().getModel());
//    this._oPopover.bindElement("/ProductCollection/0");
     this._oPopover.openBy(oEvent.getSource());
   },
   handleEmailPress: function (oEvent) {
     this._oPopover.close();
     jQuery.sap.require("sap.m.MessageToast");
     sap.m.MessageToast.show("E-Mail has been sent to bince");
   },
   handleLogOff: function (oEvent) {
     this._oPopover.close();
     jQuery.sap.require("sap.m.MessageToast");
     sap.m.MessageToast.show("Logged Off Succefully");
   },
   handleEditPress : function (evt) {
     var oTileContainer = this.getView().byId("container");
     var newValue = ! oTileContainer.getEditable();
     oTileContainer.setEditable(newValue);
     evt.getSource().setText(newValue ? "Done" : "Edit");
   },
   handleTileDelete : function (evt) {
     var tile = evt.getParameter("tile");
     evt.getSource().removeTile(tile);
   },
});




Now a custom style sheet is being declared for some hovering effects in my application.

Custom.css (I have added an animate custom code inorder to animate a jpg file in a popup.The screenshots can be found at the bottom of the blog)

.table_hover and .list_hover is used to give a custom hovering effect on mouseover of rows of the table and list.

Capture32.png


.animate_icon {
    border: 3px solid #999;
    -webkit-border-radius: 30px;
    height: 18px;
    width: 18px;
    position: absolute;
    left:90px;
    top:80px;
    -webkit-animation: pulsate 1s ease-out;
    -webkit-animation-iteration-count: infinite;
    opacity: 0.0
}
@-webkit-keyframes pulsate {
    0% {-webkit-transform: scale(0.1, 0.1); opacity: 0.0;}
    50% {opacity: 1.0;}
    100% {-webkit-transform: scale(1.2, 1.2); opacity: 0.0;}
}



To assign the hovering effect to the Table or List use the following format as shown in the screenshot


Capture33.PNG


The custom css file which i have created now has to be called during runtime of the UI5 application.So we need to make the index.html file to call the custom css file.Which can be achieved by using the code


<link type=”text/css” rel=”stylesheet” href=”Css/custom.css” />




<!DOCTYPE html>
<html>
  <head>
  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
  <meta charset="UTF-8">
        <link type="text/ccss" rel="stylesheet" href="Css/style.css" />
        <link type="text/css" rel="stylesheet" href="Css/custom.css" />
  <title>My Fiori 0</title>
  <script
  id="sap-ui-bootstrap"
  src="resources/sap-ui-core.js"
  data-sap-ui-theme="sap_bluecrystal"
  data-sap-ui-libs="sap.m"
  data-sap-ui-xx-bindingSyntax="complex"
  data-sap-ui-resourceroots='{
  "sap.ui.demo.myFiori": "./"
  }' >
  </script>
  <script>
  new sap.m.Shell({
  app : new sap.ui.core.ComponentContainer({
  name : "sap.ui.demo.myFiori"
  })
  }).placeAt("content");
  </script>
  </head>
  <body class="sapUiBody" id="content">
  </body>
</html>



Mock Json files is being used for dummy datas

mock.json (This mock.json file is used to define the Tile types and load the contents of the tiles shown in the screenshots,the mock json file is binded to the view in the Component.js file)


Capture34.PNG


{
  "My_Tile_Data": [
  {
  "Name":"Bince Mathew",
  "CreatedByBp": "Bince",
  "ChangedByName": "Bince",
  "Email": "bincemathew@gmail.com",
  "TileCollection" : [
    {
      "icon" : "sap-icon://flight",
      "title" : "Flights Available ",
      "info" : "Saudi Airlines",
      "infoState" : "Best Rates"
    },
    {
      "icon" : "sap-icon://employee",
      "title" : "My Bookings ",
      "info" : "Booking Status",
      "infoState" : "Check Your Ticket Status"
    },
    {
      "icon" : "sap-icon://employee",
      "title" : "My Bookings ",
      "info" : "Booking Status",
      "infoState" : "Check Your Ticket Status"
    },
    {
      "icon" : "sap-icon://employee",
      "title" : "My Bookings ",
      "info" : "Booking Status",
      "infoState" : "Check Your Ticket Status"
    },
    {
      "icon" : "sap-icon://employee",
      "title" : "My Bookings ",
      "info" : "Booking Status",
      "infoState" : "Check Your Ticket Status"
    }  
   ] ,
  "TileCollection_Standard" : [
  {
  "icon" : "flight",
  "number" : "1",
  "numberUnit" : "Flights",
  "title" : "Flights Available",
  "info" : "Departing in next 12 Hours"
  },
  {
  "icon" : "inbox",
  "number" : "3",
  "title" : "Ticket Status",
  "info" : "Waiting List",
  "infoState" : "Error"
  },
  {
  "type" : "Create",
  "icon" : "calendar",
  "title" : "Book Tickets",
  "info" : "Seats Available",
  "infoState" : "Success"
  },
  {
  "icon" : "loan",
  "number" : "180",
  "numberUnit" : "euro",
  "title" : "Refund Tickets",
  "info" : "1 day ago"
  },
  {
  "icon" : "loan",
  "number" : "2500",
  "numberUnit" : "euro",
  "title" : "My Wallet",
  "info" : "6 Transactions"
  },
  {
  "icon" : "inbox",
  "type" : "Monitor",
  "title" : "Saved Tickets"
  },
  {
  "icon" : "meal",
  "type" : "Monitor",
  "title" : "Food"
  },
  {
  "icon" : "stethoscope",
  "number" : "",
  "title" : "Travel Insurance",
  "info" : "Expired",
  "infoState" : "Error"
  },
  {
  "icon" : "pie-chart",
  "number" : "8",
  "title" : "My Travels Overview",
  "info" : "8 This month",
  "infoState" : "Warning"
  },
  {
  "icon" : "cart",
  "number" : "",
  "numberUnit" : "",
  "title" : "Shop With Us",
  "info" : "Over 2000 items",
  "infoState" : "Success"
  },
  {
  "icon" : "factory",
  "number" : "8",
  "numberUnit" : "Services",
  "title" : "Other Services",
  "info" : "",
  "infoState" : ""
  },
  {
  "icon" : "calendar",
  "title" : "Travel Planner"
  }
  ]
  }
  ]
}




Fragment Codes ( All the codes used in each fragments is listed below)


Footer.fragment.xml


<core:FragmentDefinition xmlns="sap.m"
  xmlns:core="sap.ui.core"
  controllerName="sap.ui.demo.myFiori.view.Detail">
  <Bar>
  <contentRight>
  <Button text="Edit" press="handleEditPress" />
  <Button text="Alert Box" press="onAlert" />
  <Button text="Open Popup" icon="sap-icon://notes" press="onMsgDialogPress" />
  <Button text="Dynamic Icons" icon="sap-icon://create"
  press="openDialog1" />
  </contentRight>
  </Bar>
</core:FragmentDefinition>



MainContent.fragment.xml



<core:FragmentDefinition xmlns="sap.m"
  xmlns:core="sap.ui.core"
  controllerName="sap.ui.demo.myFiori.view.Detail">
<List id="Menu" headerText="" >
<items>
<ActionListItem title="Flights" text="Book Flights" class="list_hover"/>
<ActionListItem title="My Bookings" text="My Bookings" class="list_hover"/>
<ActionListItem title="Status" text="Flight Status" class="list_hover"/>
<ActionListItem title="Refund" text="Refund Tickets" class="list_hover"/>
</items>
</List>
</core:FragmentDefinition>


MsgDialog.fragment.xml


<core:FragmentDefinition xmlns="sap.m"
  xmlns:core="sap.ui.core">
  <Dialog title="Dynamic Icons" class="sapUiPopupWithPadding" contentWidth="0em"
  contentHeight="10em">
  <Page showHeader="false">
  <content>
  <Image src="Images/Fiori.jpg" width="25em" height="9em"
  densityAware="false" align="right" />
  <Image class="animate_icon" src="Images/heart.png" width="2em"
  height="2em" densityAware="false" />
  </content>
  </Page>
  <beginButton>
  <Button type="Accept" text="Accept" press="closeButton">
  <layoutData>
  <FlexItemData growFactor="1" />
  </layoutData>
  </Button>
  </beginButton>
  </Dialog>
</core:FragmentDefinition>


Popover.fragment.xml


<core:FragmentDefinition
  xmlns="sap.m"
  xmlns:core="sap.ui.core">
<Popover
    title="Bince"
    class="sapUiPopupWithPadding"
    placement="Left" >
<Image src="Images/bince.jpg" width="10em" height="10em" densityAware="false" />
    <footer>
      <Bar>
      <contentLeft>
<Button
            text="Profile"/> 
      </contentLeft>
        <contentMiddle>
          <Button
            text=""
            icon="sap-icon://email"
            press="handleEmailPress" />
        </contentMiddle>
        <contentRight>
        <Button text="Logout" press="handleLogOff" type="Reject"></Button>
        </contentRight>
      </Bar>
    </footer>
  </Popover>
</core:FragmentDefinition>


Popup.fragment.xml


<core:FragmentDefinition xmlns="sap.m"
  xmlns:core="sap.ui.core"
  controllerName="sap.ui.demo.myFiori.view.Notes">
  <Dialog title="My Popup" type="Message">
  <VBox >
  <items>
  <Label text="Comments" />
            <TextArea width="100%" height="100%"></TextArea>
  </items>
  </VBox>
  <beginButton>
  <Button text="Ok" press="onDialogokButton" />
  </beginButton>
  <endButton>
  <Button text="Cancel" press="onDialogCloseButton" />
  </endButton>
  </Dialog>
</core:FragmentDefinition>


Sidepanel.fragment.xml


<core:FragmentDefinition xmlns="sap.m"
  xmlns:core="sap.ui.core"
  controllerName="sap.ui.demo.myFiori.view.Detail">
<List id="User_Settings" headerText="">
<items>
<ActionListItem title="Settings" text="My Account Details" class="list_hover"/>
<ActionListItem title="Profile"  text="View Profile" class="list_hover"/>
<ActionListItem title="Payment"  text="Add A Payment Method" class="list_hover"/>
<ActionListItem title="History"  text="My Booking History" class="list_hover"/>
<ActionListItem title="Cancel"   text="Ticket Cancellation" class="list_hover"/>
<ActionListItem title="Stay"     text="Book Accomodation" class="list_hover"/>
<ActionListItem title="Services" text="Platinum User Services"  class="list_hover"/>
<ActionListItem title="Reports"  text="My Travel Analytics"     class="list_hover"/>
</items>
</List>
</core:FragmentDefinition>


Tiles.fragment.xml


<core:FragmentDefinition xmlns:core="sap.ui.core"
  xmlns:mvc="sap.ui.core.mvc" xmlns="sap.m" controllerName="view.search"
  xmlns:html="http://www.w3.org/1999/xhtml">
  <Page  id="r1" showHeader="false" enableScrolling="false">
<!-- <Label text="My Flight" class ="content7"/> -->
  <TileContainer id="container"  class ="content8"
  tiles="{/My_Tile_Data/0/TileCollection_Standard}">
<StandardTile
        icon="sap-icon://{icon}"
        type="{type}"
        number="{number}"
        numberUnit="{numberUnit}"
        title="{title}"
        info="{info}"
        infoState="{infoState}" />
  </TileContainer>
  </Page>
</core:FragmentDefinition>


In addition to these we can also create custom controls like a button or panel from scratch and use it a new UI control in our applications.

It basically involves two main parts.

  1. Extending the sap.ui.core.Control to define the metadata of the new control you are trying to build with its own custom properties,aggregations  and events.
  2. Using a renderer to render your final custom control using the metadata you have defined.

Capture35.PNG

More detailed article about creating a custom control can be found in the following link Creating a reusable UI5 tile control – SAPUI5 control design on the fly



Final UI5 Application Outputs

Capture23.PNG

Capture24.PNG

Capture25.PNG

Capture26.PNG

Capture27.png

Capture28.PNG

Capture29.PNG

Regards,

Bince Mathew C

To report this post you need to login first.

5 Comments

You must be Logged on to comment or reply to a post.

  1. Abdulkadir AKIN

    Very good demostration Bince  .

    How can i use your tile page for navigate between a few ui5 applications. The every tile will call different ui5 app (as fiori launchpad), not to views.

    By the way, can you please share the project files ?

    Thanks.

    (0) 

Leave a Reply