Quantcast
Channel: SAPUI5 Developer Center
Viewing all 789 articles
Browse latest View live

How to add the Fiori / Unified Shell

$
0
0

Hi,

 

Hope you all have followed previous tutorials successfully!

Start with the SAP Web IDE On-Premise

Connect the SAPWebIDE On-Premise to your SAP Gateway system On-Premise

Generate your custom Fiori application with SAPWebIDE On-Premise using Gateway On-Premise

Navigate between single full view and split view

Organize your UI5 project

 

In this last part of the tutorial series we are going to add the final touch to finish our custom fiori application! We are going to add the Unified Shell! This is also used in Fiori applications.

 

Enjoy the show

 

 

Last tutorial for 2014! Happy Holidays!

 

Kind regards,

Wouter


Setting up Virus scan profile /SCET/GUI_UPLOAD in Gateway

$
0
0

If virus scan profile is not set up properly then while uploading files through NetWeaver Gateway (File Upload/Download through NetWeaver Gateway) it will throw an error “Virus scan profile /SCET/GUI_UPLOAD is not active” as below. We worked with our basis team to get it resolved. Here are the steps.

 

 

Error:

1.png

 

Solution Steps:


1)  Go to transaction 'VSCANPROFILE'  and make sure /SCET/GUI_UPLOAD profile is not active.

 

2.png

 

2)  Go to trtransaction:  /n/IWFND/VIRUS_SCAN (Configuration of virus scan profile for gateway).

 

      Current Settings: Virus scan profile is switched  on for /SCET/GUI_UPLOAD.

3.png

3)  Remove virus scan profile and Switch off virus scan check box. Execute.


4.png

This message should appear.

5.PNG


Now virus scan issue should be resolved and you should get a HTTP status code 204.


6.PNG

Why my formatter does not work? A trouble shooting example to know how it works

$
0
0


Recently I am following the exercise Building SAP Fiori-like UIs with SAPUI5( it is really a fantastic guide ) and I meet with trouble in EXERCISE 3 – FORMATTER.


I just copy the source code from the guide:

clipboard1.png

Unfortunately my formatter statusText which is used to format LifecycleStatus in Master page is not called in runtime at all:

clipboard2.png

And to my surprise,the same syntax for date formatter can work perfectly in detail page. Why?

clipboard3.png

Why the formatter for status is not called at all

 

Since none of other SCNers has complained about this issue, so I assume there must be something wrong in my own code. So I began to debug to compare why formatter for CreatedAt works.

 

Soon I found out how formatter for CreatedAt works. In the runtime, firstly the raw value of field CreatedAt is fetched from line 22833 and stored in variable oValue, and then passed to its formatter in line 22838.

The "this.fnFormatter" actually points to the formatter scn_exercise.util.Formatter.date defined in my code.


clipboard4.png

However, for the failure case,the framework didn't recognize any formatter for LifecycleStatus,so my formatter is not called at all.

clipboard5.png

How does framework parse xml view to get metadata such as formatter information

 

The screenshot below contains a good entry point for xml view parse debugging.The XMLTemplateProcessor-dbg.js contains the implementation to parse xml source code and create UI5 control accordingly.

clipboard6.png

from the callstack we know its method handleChildren, createControls etc is recursively called to handle with  controls defined hierarchically in your xml view.

clipboard7.png

Finally I reached the code below. After line 21082 is executed, the formatter for field CreatedAt will be parsed.


clipboard8.png

I will explain how the reference pointing to my formatter for CreatedAt is parsed via text.

 

before the for loop, variable oObject points to the global window object, since no context exists in this case.

 

The first for loop: i = 0, execute line 15162, aNames[0] = "scn_exercise", so after that oObject now points to window.scn_exercise.

 

The secondfor loop: i = 1, aNames[1] = util, so after line 15162 oObject points to window.scn_exercise.util.

 

The third loop: i = 2, aNames[2] = Formatter, so after line 15162 oObject points to window.scn_exercise.util.Formatter


The fourth loop: i = 3, aNames[3] = date, so after line 15162 oObject points to window.scn_exercise.util.Formatter.date.Then quit for loop.

clipboard9.png

why the formatter for Lifecyclestatus is failed to be parsed?

 

based on the previous analysis on formatter for CreatedAt, we can easily figure out what is wrong.

In the case for Lifecyclestatus,there is no attribute named "util" under window.scn_exercise, instead onlyu a dummy one "Component".

clipboard10.png


just compare the correct case for CreatedAt,where all our formatters are existing under window.scn_exercise.

clipboard11.png

When the attribute util become available under window.scn_exercise

 

Since I have defined the usage of formatter implementation in detail view's controller:

jQuery.sap.require("scn_exercise.util.Formatter");

 

the js file will be downloaded via AJAX and execModule is called on it after a successful download:

clipboard12.png


the js source code is executed via eval:

clipboard13.png

after that the util is available in window.scn_exercise:

clipboard14.png

then finally I find the root cause: I forget to add the following line in my master controller. Once I have added this, the issue is gone.

clipboard15.png

Hope this blog can give you some hint to do trouble shooting by yourself when you meet with the formatter or xml view parse issue

splitApp with Routing Mechanism

$
0
0

In this blog, you will find details about how to build a splitApp mobile application using SAPUI5 and also how to use routing mechanism to navigate between different views. Before reading further please look at the below video, which shows how the final application looks like SAPUI5 splitApp with Routing mechanism - YouTube.Also download the complete project MyRoutingDemo.zip - Google Drive and import into your Eclipse IDE. It helps having the code with you while reading the blog.


I assume that you have basic knowledge of SAPUI5 and developed some basic applications using it in Eclipse. The application has total five JS type views and their corresponding controllers.

-Empty view – No content

-MainView view –  This view holds splitApp control

-Years view – This view displays years

-Categories view – This view displays difference categories like Movie, Actor etc.,

-NomandWinner view – This view displays nominees and winner for a particular category

 

I have created a JSON file which holds model data. While I am talking about JSON model, I have to mention about website http://www.jsoneditoronline.org/ . I have used this website to create my JSON model, this has very simple editor and you can see JSON file in terms of individual objects in it.

 

Let's look at the JSON model data.json file

 

Here I have given a snapshot of one element of the model

 

Untitled.png

'oscars' node has 5 objects, each one in turn has 2 objects one of them is categories object, which has again 3 objects, where 'nominee' is one of them. Nominee object in turn has 3 objects, where each of them has single element called 'nominee'.  ( I know how you feel after reading this explanation sorry for that )

 

Let’s say I want to read different categories for year '2010', then I have to go to ‘0’ element of array ‘oscars’ and then access ‘categories’ element and then access ‘category’ element. In terms of path definition this can be represented as ‘oscars/0/categories’

 

If I want to read different nominee for a ‘movie’  category in year 2010 then it will be ‘/oscars/0/categories/0/nominees’

 

Let’s look at different files in the project I have written comments for each line  in the code so I am not going to explain them here again.

 

index.html

Router declaration and initialization happens in index.html file.

 

<!DOCTYPE HTML><html>  <head>  <meta http-equiv="X-UA-Compatible" content="IE=edge">  <meta http-equiv='Content-Type' content='text/html;charset=UTF-8'/>  <script src="resources/sap-ui-core.js"  id="sap-ui-bootstrap"  data-sap-ui-libs="sap.m,sap.ui.commons"  data-sap-ui-theme="sap_bluecrystal">  </script>  <!-- only load the mobile lib "sap.m" and the "sap_bluecrystal" theme -->  <script>  sap.ui.localResources("Views");  //Folder where views JS files are stored  jQuery.sap.require("sap.ui.core.routing.Router");  // load Router module  jQuery.sap.require("sap.m.routing.RouteMatchedHandler");  // load RouteMatchedHandler module  //Initilaize the MainView  var app = sap.ui.view({id:"idMainView1", viewName:"Views.MainView", type:sap.ui.core.mvc.ViewType.JS});  //Place the MainView content in "Content" Area  app.placeAt("content");  // Create an instance of Router class  var oRouter = new sap.ui.core.routing.Router(  {      rcategories: {  //Route 'rcategories' takes you from the current view to view 'Categories'          pattern: "oscars/{year}",  // This pattern is placed after the hash('#') symbol in URL                                      // varaible inside {}, 'year' in this case is replaced with value later                                      // in method itempressed() inside controller of 'Years' view          view: "detail.Categories",  // This is the target view, to where navigation happens  },  rnomwinner: {    //Route 'rnomwinner' takes you from the current view to view 'NomWinner'  pattern: "nominees/{year}/{cat}",  // This pattern is placed after the hash('#') symbol in URL      // varaible inside {}, 'year' and 'cat' in this case are replaced with values later                                      // in method itempressed() inside controller of 'Categories' view  view: "detail.NomWinner"  ,        // This is the target view, to where navigation happens  }  },  {  targetControl: "splitapp",      // targetControl refers to the id of control where all the target views are to be placed                                  // this is defined in 'MainView' createcontent method  targetAggregation: "detailPages", // targetAggregation refers to aggregation namw of targetControl. For splitApp                                  // control there are two Aggregations, 'detailPages' and 'masterPages'  viewType: "JS",  // viewType defines type of the target views  viewPath: "Views",  // The view path parameter specifies a prefix that prepends the target views. In our case                    // target view detail.Categories will be takes as Views.detail.Categories. Please note that                    // view Categories is placed under folder Views/detail/  clearTarget: false  //The clear target parameter defines a boolean that is used to specify if the aggregation                      //should be cleared before adding the view to it.  });  var oRouteHandler = new sap.m.routing.RouteMatchedHandler(oRouter);  oRouter.register("router");  // Assign a name to Router, so that we can access it in all controllers by using this name  oRouter.initialize();  // Initialise the Router  </script>  </head>  <body class="sapUiBody" role="application">  <div id="content"></div>  </body></html>

Years View

 

createContent : function(oController) {
// Create a standardlistitem control by binding 'title' property with element 'year' of the JSON model  var oItemTemplate = new sap.m.StandardListItem({title : "{year}", type : sap.m.ListType.Active});
// Create 'List' control having 'oscars' node of model as binding to 'items' aggregation and add a
// eventhadnler method ( 'itempressed' method in controller) to 'itemPress event. This event gets triggered
// whenever user press a Listeitem  var oList = new sap.m.List({  items : {path:"/oscars", template:oItemTemplate},     itemPress : function(oEvent){  oController.itempressed(oEvent);  }  });
// Add List control to a new Page control and return it  var oPage = new sap.m.Page({  title: "Years",  content: [oList]  });  return oPage;  }
});

Year view Controller

 

// This method gets triggered whenever a item in the List is pressed. Here we trigger
// navTo() method of Router to initiate the navigation
itempressed: function(oEvent) {   // Get instance of router   this.oRouter = sap.ui.core.routing.Router.getRouter("router");
// Get Array if all items in List control   var a = oEvent.getSource().getAggregation("items");
// Get reference to current Listitem that has been clicked   var b = oEvent.getParameters().listItem;
// In order to know the position of the clicked Listitem with in the List
// we loop over all items and compare each Listitem with the clicked Listitem   for (var i = 0; i < a.length; i++) {  if (a[i] == b) {  break;  // we have a match so exit the loop. At this point 'i' refers to index of clicked Listitem  }   }
// Call navTo() method of Router class to trigger the navigation
// For route "rcategories' pattern is defined as 'oscars/{year} where 'year' is a variable
// which needs to be repalced by a value. Here we are replacinf it with the index of Listitem
// that has been clicked by user   this.oRouter.navTo("rcategories", { year : i });   }
});

Categories View

 

createContent : function(oController) {
// Create a standardlistitem control with id 'idlistitem' and  by binding 'title' property with element
// 'category' of the JSON model
var oItemTemplate = new sap.m.StandardListItem(this.createId("idlistitem") , {title : "{category}", type : sap.m.ListType.Active});
//Create 'List' control with id 'idlist' and add a
//eventhadnler method ( 'itempressed' method in controller) to 'itemPress event. This event gets triggered
// whenever user press a Listeitem
var oList = new sap.m.List(this.createId("idlist"), {
itemPress : function(oEvent){
oController.itempressed(oEvent);  }  });
//Add List control to a new Page control and return it
var oPage = new sap.m.Page({  title: "Categories",  content: [oList]
});
return oPage;  }
});

Categories View Controller

 

onInit: function() {
// Get instance of Router  this.oRouter = sap.ui.core.routing.Router.getRouter("router");
// Call 'attachRouteMatched method,which defines a function that gets called everytime
// a 'RouteMatched' event is triggered by application  this.oRouter.attachRouteMatched(function(oEvent) {
// get the name of the Route   var sRoute = oEvent.getParameter("name"),
// Get the name of the target view       oView = oEvent.getParameter("view");
// As this method gets called for all routes defined in the application we  have to
//make sure that code gets executed only for the route that we are interested in
if(sRoute == "rcategories")
{
// Define the binding path
var spath = "/oscars/" + oEvent.getParameter('arguments').year + "/categories";
// Bind aggragation 'Items' of List with above defined path pass listitem with id 'idlistitem' as template
oView.byId("idlist").bindItems(spath, oView.byId("idlistitem"));
}   });  },
itempressed: function(oEvent) {      //Get the instance of Router   this.oRouter = sap.ui.core.routing.Router.getRouter("router");  //Get the instance of HashChanger   this.oHasher = sap.ui.core.routing.HashChanger.getInstance();   //Get the current hash value in the URL and split the URL at character '/'   // if the hash value is 'oscars/2', then below statemnet will split that into   //'oscars' and '2'. we are interested in element with index 1 i.e. second element in array 'j'   var j = this.oHasher.getHash().split("/")[1];   // Get all instances of Listitems present in List   var a = oEvent.getSource().getAggregation("items");   //Get instance of Listitem which user has clicked   var b = oEvent.getParameters().listItem;   //To get the index of the Listitem that has been clicked loop over the array of Listitems   // and compare with clicked listitem that is 'b'   for (var i = 0; i < a.length; i++) {  if (a[i] == b) {  break;  //we have a match so exit the loop  }   }  //navigate to Route 'rnomwinner'. pass values to variables 'year' and 'cat' in patter 'nominees/{year}/{cat}'   this.oRouter.navTo("rnomwinner",  { year : j , cat : i});   }
});

NomWinner view

 

createContent : function(oController) {
// Create a standardlistitem control with id 'idlistitem' and  by binding 'nominee' property with element
// 'category' of the JSON model
var oItemTemplate = new sap.m.StandardListItem(this.createId("idlistitem"), {title : "{nominee}", type : sap.m.ListType.Active});
//Create 'List' control with id 'idlist'  var oList = new sap.m.List( this.createId("idlist"));
//Create a Button control with id 'idbutton' and add an eventhandler method 'click' of controller
// to action 'press'  var oButton = new sap.ui.commons.Button(this.createId("idbutton"),{  width : "300px",  height : "100px",  style: sap.ui.commons.ButtonStyle.Accept,  press : function(oEvent) {  oController.click(oEvent);  }  });
//Add List control and Button control to a new Page control and return it  this.page = new sap.m.Page({  title: "Nominees and Winner",  navButtonText: "Back",  showNavButton: true,  navButtonPress: [oController.onNavButtonTap, oController],  content: [oList, oButton]  });  return this.page;  }
});

NomWinner view Controller

 

onInit: function() {
// Get instance of Router   this.oRouter = sap.ui.core.routing.Router.getRouter("router"); 
// Call 'attachRouteMatched method,which defines a function that gets called everytime
// a 'RouteMatched' event is triggered by application    this.oRouter.attachRouteMatched(function(oEvent) {
// Get the name of the Route   var sRoute = oEvent.getParameter("name"), 
// Get the name of the target view   oView = oEvent.getParameter("view"); 
// As this method gets called for all routes defined in the application we  have to
//make sure that code gets executed only for the route that we are interested in    if(sRoute == "rnomwinner")  {
// Define the binding path    var spath = "/oscars/" + oEvent.getParameter('arguments').year + "/categories/" + oEvent.getParameter('arguments').cat + "/nominees";
// Set the button text back to original value every time user clicks on new category   oView.byId("idbutton").setText("And the Winner is....");
// Bind aggragation 'Items' of List with above defined path pass listitem with id 'idlistitem' as template   oView.byId("idlist").bindItems(spath, oView.byId("idlistitem"));  }   });  },
click: function(oEvent){
//Get the instance of HashChanger   this.oHasher = sap.ui.core.routing.HashChanger.getInstance();
//Split the hash value at '/'. this will give you an array with individual values    var j = this.oHasher.getHash().split("/");
//Get the value of element 'winner' in JSON model at a given path    var winner = sap.ui.getCore().getModel().getProperty("/oscars/"+j[1]+"/categories/"+j[2]+ "/winner");
//Set the value of property 'text' of button with value of 'winner'    this.getView().byId("idbutton").setText(winner);  },
// Navigate previous entry in browser history
//This function is called when user clicks on back button in detail view
onNavButtonTap : function()  {  window.history.go(-1);  }
});

I would like to discuss more about how routing works rather than explaining how the different views are built. Let's divide the routing into different parts and analyse each part in detail.

 

1 - Definition and initialization of Routes

 

Routes definition consists of giving a name to the route, name of the target view and pattern value if needed.Now there are three statements that we need understand

 

sap.m.routing.RouteMatchedHandler(oRouter)–   Create an instance of RouteMatchedHandler class. Inside the constructor, two standard methods are attached to  two events ‘RouteMatched’ and ‘RoutePatternMatched’. These method handle the placement of the target view based on the configuration parameters ‘targetControl’ and ‘targetAggregation’ if the view needs to be placed in masterPages or ‘detailPages’

 

oRouter.register("router") - Registers the router to access it from another context. Use sap.ui.routing.Router.getRouter() to receive the instance, bu passing ’router’ as the value.

 

oRouter.initialize() -  Attaches the router to the hash changer. In this method we create instance of sap.ui.core.routing.HashChanger and attach event handler function to event ‘hashChanged’.

 

 

2-  How navigation happens using routes

 

In any event handler method inside the controller of source view we should call method "navTo" of class sap.ui.core.routing.Router. In our case to navigate to 'Categories' view we use below code in event handler method 'itempressed' inside controller of view 'Years'

 

  this.oRouter.navTo("rcategories", { year : i });

 

Here first parameter takes name of the route and second parameter is used to fill in the variables  in the 'pattern' property of the specific route. Route rcategories has pattern 'oscars/{year}'. Here variable 'year' is replaced with value 'i' and this pattern is placed after '#' in the URL. When I click on 2011, value after # is changed to 'oscars/1' as shown below.

 

Capture.JPG

Similarly if we click on 'Actress', itempressed() method in controller of view 'Categories' is called and there we have the below code

 

this.oRouter.navTo("rnomwinner",  { year : j , cat : i});

 

If you look at the definition of Route 'rnomwinner' ( nominees/{year}/{cat }), we see two variables 'year' and 'cat'. So in the 'navTo' method we pass values to these two variables. Below is how the URL looks once we click on any of the category

 

Capture.JPG

Now let's look at how navTo() method actually works. below is the call stack ( I have added only important methods )

 

MethodClass
navTo() - Initiates the navigation to target viewsap.ui.core.routing.Router
setHash - change hash value to new valuesap.ui.core.routing.HashChanger
fireHashchanged - fire event 'HashChanged'sap.ui.core.routing.HashChanger
RouteMatched(Inside this method 'Createcontent()' method of the target view is called)sap.ui.core.routing.Route
fireRoutematched - Fire event 'RouteMatched'sap.ui.core.routing.Router
attachRouteMatched - In this method we can write custom logic on what to do when hash value matches with a ‘pattern’ value of certain route ( for example, bind a property of control with an element /collection of model. In our case we bind the aggregation ‘items’ of ‘List’ control with ‘categories’ node)In target view controller ( in our case it is placed in oninit() method of target view controller

 

So to summarize navTo() method results in following

  • Set the hash value to new value
  • Find out all the routes that have pattern value which matches with the new hash value. Once we have match, fire 'routematched' event, which calls all 'attachroutematched' methods

 

Please note that I am no way an expert on SAP UI5, all that is presented here is done by reading/exploring stuff on my own. If there are any mistakes or blunders that I did, please feel free to comment. I really appreciate that.

 

Thanks for reading my blog and keep sharing

SAP WebIDE feedback

$
0
0

     Hello everyone! I've been using recently the WebIDE (or River RDE as it was called previously) and I thought I should share a few positive and negative points about my experience. I've also developed a few UI5 applications using Eclipse previously and I must admit that this new environment offers some great extra features. For those who do not know, there are two main ways (which I know of) of using the WebIDE: either from a free Hana Cloud Platform account or from a local installation. I've managed to use both types of installs and they differ by just a few things (e.g. on the local install you have less templates).

  1. Positive aspects:
    • Much faster /better content assists while editing js and xml files
    • API reference pane (if opened and you press Ctrl + Space you get the complete reference for the class / .xml tag you were writing)Untitled.png
    • Better code syntax checking, unused variable warnings, etc.
    • The layout editor (left click on a view -> open with -> layout editor; it's a what-you-see-is-what-you-get, drag-and-drop editor for views)
    • Some common templates are available when creating a new project; you can also add some of your own templates
    • Code formatter (the beautify command), git and SAP Jam integration (although I haven't managed to try out SAP Jam, it seems like a helpful idea)
    • Several testing / running options: run with/without frame ('with frame' is good for testing your application on different resolutions, 'without frame is good for debugging the .js), with mock data, on the cloud or on the ABAP server (if you have a connection to one)
    • You can test your code much faster and you don't need to worry about Apache servers or other stuff (like in the case of Eclipse)
    • Code completion as you type (sometimes it can be annoying though)
      Capture.PNG
    • You can connect the WebIDE to a on-premise ABAP system (I have tested with both the local and the cloud version of the IDE) and if you implement a couple of SAP Notes on the system, you will have full transport/package support.
    • When using the cloud version, the UI5 library is always up to date automatically (no need to track new releases and download libraries)
  2. Negative aspects / bugs:
    • As any HTML5 web based IDE, sometimes it just says that the session expired, and that you should copy your work to an external file and refresh the page... So you should always save files.
    • Sometimes left clicks do not work or respond very slowly (e.g. on menu items)
    • The syntax checks do not always give clear error descriptions, global variables also produce warnings, so you have to declare them as global variables in comments (e.g. /*global variable_name */) or you can disable the syntax checking for that block (with /* eslint-disable */ )
    • The outline panel does not seem to work
    • The IDE does not have a feature for saving and inserting code snippets / small code templates (or I haven't found it)
    • Auto-complete / ctrl + space do not work (yet) for .html files
    • Sometimes the code completion / parameter hints 'hang' and are 'left behind'
      Capture.PNG
    • Once in a while, some of the keyboard shortcuts stop working (e.g. CTRL+SHIFT+A for the API reference)
    • In very rare cases, I would get a 'Cannot access property "length" of undefined' error in the IDE and a pane would then disappear (it happened to me with the development -left-  pane). Of course, the error did not persist after I saved and refreshed the IDE page.
    • Sometimes you may develop an application for a specific system which has a specific version of the UI5 runtime. It would be nice if the IDE would allow you to select from a list of (major) UI5 library versions on which to test your application.
    • The layout editor currently only supports views based on sap.m.Page (so only for some mobile apps)

 

     Overall the IDE is a very good tool, but it sometimes lacks stability and still has some issues to address. But it is a very good start and should clearly be used by anybody currently developing a Fiori / UI5 application.

Build & Versioning SAPUI5 Application in Maven

$
0
0

There are two ways to build SAPUI5 application in Maven.


1)  As explained by Ted Castelijns in How to use Maven to build your SAPUI5 application

2)  Simply convert and build SAPUI5 application in Maven. I will explain it in this blog


In option 1 you need to add SAPUI5 libraries to local Maven repository. I found it little tedious as it is not an one time job, we need to update it whenever there is a new version of SAPUI5 library available. The second option may not be a typical way of Maven build where you can specify SAPUI5 dependencies as well but it does the build & versioning job.

 

Assumptions:

  1. Maven plugin is installed in your eclipse. Note: It comes by default with Eclipse Luna onwards.
  2. SAPUI5 libraries are installed in eclipse

 

Create a SAPUI5 application. This is how a basic SAPUI5 project looks like, I have just replaced the generated folder with "app" folder, it has all sub-folders for the app. You are free to create your folder structure the way you want it but remember to change the assembly.xml file accordingly as below.

1.png

1) Convert it to a Maven project in Eclipse (Right click on project->Configure->Convert to Maven project), POM (pom.xml) wizard will pop up, change accordingly as in screen shot below.

 

2.png

3.png

Now the project is converted into a Maven project and pom.xml file is created.

 

 

There are few steps here:

1) Refer the pom.xml below and insert the build tag.

 

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">  <modelVersion>4.0.0</modelVersion>  <groupId>SAPUI5_Maven</groupId>  <artifactId>SAPUI5_Maven</artifactId>  <version>1.0.0.001</version>  <packaging>pom</packaging>  <name>SAPUI5toMaven</name>  <description>SAPUI5 to Maven</description>  <build>  <sourceDirectory>./WebContent</sourceDirectory>  <plugins>  <plugin>  <artifactId>maven-clean-plugin</artifactId>  <version>2.4.1</version>  <executions>  <execution>  <id>clean</id>  <phase>clean</phase>  <goals>  <goal>clean</goal>  </goals>  </execution>  </executions>  </plugin>  <plugin>  <artifactId>maven-release-plugin</artifactId>  <version>2.4.2</version>  <configuration>  <releaseProfiles>release</releaseProfiles>  <goals>deploy assembly:single</goals>  </configuration>  </plugin>  <plugin>  <groupId>org.apache.maven.plugins</groupId>  <artifactId>maven-assembly-plugin</artifactId>  <executions>  <execution>  <id>create-webcontent-distribution</id>  <phase>package</phase>  <goals>  <goal>single</goal>  </goals>  <configuration>  <finalName>WebContent</finalName>  <appendAssemblyId>false</appendAssemblyId>  <descriptors>  <descriptor>assembly.xml</descriptor>  </descriptors>  </configuration>  </execution>  </executions>  </plugin>  </plugins>  </build></project>

2) Insert the file to your project at the same level as pom.xml. assembly.xml files is easy to understand. It builds the Target folder. It has the source directory/file path to Target directory/file mapping

3) Insert manifest.properties file (attached) at index.html level. It contains the app version number.

4) Go to your work space, find your project and insert .Ui5RepositoryTextFiles file (attached) in WebContent folder. I will explain the use of it when we upload/deploy the project in my next blog.


Now the project should look like this:

4.PNG

Project is ready to be build by Maven now.

1) Right click on project->Run As->Maven build .

2) Then "clean install" in the pop up->Run. Check the console for the build log "BUILD SUCCESS" should come up.

5.PNG

6.png

7.PNG

 

Refresh the project to see the Target folder with version number, folders & files as per assembly.xml file.

 

8.PNG

Remember to change the version number, Maven clean & Maven build every time you change the project for every new upload. You can create a template project and use it for new projects to avoid these steps.

 

Any suggestions are welcome.

Quickly tinker around with SAPUI5 examples

$
0
0

So you want to quickly tinker around with all those cool examples from the SAPUI5 explored app, but you don’t want to set up a local IDE or create all those files manually. You want to try out all those MVC examples with the XML views online, but you don’t want to fiddle around in JSBin with this awkward string concatenation. You want a real MVC example! Sample data would also be great so that you can practice data binding.


If that is what you want - I’ve got a solution for you!


I was searching for an online JavaScript playground which supports multiple files, because SAPUI5 is a MVC framework. One solution is Plunker and also stumbled about Alexander Graebe’s blog post where he describes different JavaScript playgrounds. With Plunker you can create and share your SAPUI5 examples and also download them as ZIP files. With the SAP Web IDE you can also try out things quickly, because it is a web-based IDE.


I highlight here some things you have to be aware of when using the explored examples which apply to Plunker and the SAP Web IDE.


In the explored app you can download the examples as zip files, but you need some glue code to run those views - for those mobile examples this would be an app and a page. A best practice would be to use a Component.js set up - but let’s not complicate things at the beginning! I’ve prepared a Plunker MVC example where you can replace the code of the main View and Controller, you just need to correct the name of the Controller.



Table of contents:

  • Plunker Button example
  • Import Plunker Button example ZIP into SAP Web IDE
  • Create the example in the SAP Web IDE from Scratch
  • Things to be aware of when using snippets
  • >> DJ Adams's Fiori like exercise <<
  • Summary and useful links


 

Plunker Button example


Let’s walk through this steps with the button example from the explored app. Here is the Plunker with a data binding example loaded from an JSON file:


Plunker MVC example table with json data

For this plunk I’ve used the Table Columns example from the explored app, I just corrected the names of the controller (which I’ll describe in the following example), saved the products.json from the explored-app and put the model-creation and binding code into the index.html.



Open the Plunk and fork it so that you have your own copy:

fork_plunk.png

 

 

Then go to the explored app, look for the Button example and open the sample:

 

Explored Button example

explored_button.png

 

Go to show code:

explored_show_code.png

 

 

Select the Page.view.xml code and copy it:

explored_copy_view.png

 

 

Go to your plunk, open the view/Main.view.xml, paste the code and adjust the Controller name to "view.Main":

plunker_replace_controller.png

 

 

From the explored app copy the Page.Controller.js code:

explored_copy_controller.png

 

In the Plunk open view/Main.controller.js, replace the code and adjust the name of the Controller to "view.Main":

plunker_replace_controller_controller.png

 

And we are done!!! Choose "Run" to see your sample in action! Easy isn't it?

plunker_run.png

 

Now you can play around with the Button samples, for example change the type of the buttons or the icons.


You can downloaded the zip file you can easily import it into the Web IDE:

plunker_download_zip.png

 

 

SAP Web IDE import Plunker Button example ZIP

 

In the SAP Web IDE import choose the Local folder, right click it and create a new folder:

web_ide_file_new_folder.png

web_ide_folder_name.png

 

 

Select the folder, right click it and import the downloaded zip file:

web_ide_import_archive.png

webide_browse_import.png

 

 

Select the index.html file and choose Run:

web_ide_run.png

 

A new tab opens and displays the result. Don't close this tab! Whenever you change the source code, just choose Refresh in this preview window (not the browser refresh):

webide_preview.png



SAP Web IDE Button example from scratch


If you want to do the same example from scratch with the Web IDE, just use the handy “Project from Template” wizard and just replace and adjust the View and Controller code.


First select the Local folder and create a new project with the wizard:

web_ide_file_new_folder.png



Choose "SAPUI5 application project"

web_ide_template.png


Enter the name for the project:

web_ide_project_name.png

 

Leave the settings with "Mobile" and "XML" but change the view name to Main:

web_ide_view_name.png


After that you are finished and now you can continue to change the code as described above:

web_ide_replace_main_pfeil.png



Using snippets in your existing projects


When you are just using snippets and not the whole example, be aware to correct namespaces - otherwise your code will break!


Here you see that the snippet uses xmlns:l="sap.ui.layout" but in my example I have xmlns:sap.ui.layout=sap.ui.layout -> so you need to replace the "l" with "sap.ui.layout".


namespace.png



DJ Adams's Fiori like exercises as plunk


So enough with this simple one page apps you think? What about a full fledged app with a Master-Detail pattern? I created DJ Adams's"Building SAP Fiori-like UIs with SAPUI5 in 10 Exercises" example as a plunk so that you can play around with that. Thanks DJ for your great work!!!


Fiori-like app by DJ Adams plunk



Summary

 

I hope you can now more easily play around with the SAPUI5 library and that I could lower the entry hurdle. Shared source code with Plunker is a great way to learn SAPUI5 and I will for sure create lots of plunks this year and add those plunks in this blog post. Also if you have problems with SAPUI5 source code it is a great way to share your code.

 

 

Useful links

 


Embedding a custom CSS style class to a SAPUI5 control

$
0
0

The visual display of SAP UI5 controls can be edited in various ways. One simple way is to define an own CSS style class and to embed it to the SAP UI5 Control. It is verry simple:

Extending the SAP UI5 INDEX file

 

Add a new css class to the index.html. Of Course, there are also other ways (external css file, ... ) - but in this example we will add the style-class direct in the index.html file;

 

democssclass1

Adjusting the SAP UI5

 

Use the method addStyleClass to add the new css class to the sap ui5 control:

 

democssclass2

Result

 

Not very surprisingly, the font color is whit, black background and italic:

 

 

democssclass3


LESS within SAPUI5/HTML5

$
0
0

Hello Folks,

 

This is my first Blog with respect to UI Framework. Here is some key points and issues which i faced in SAPUI5/HTML5 with respect to UI designing, and rendering of control either in mobile/tab or laptop/desktop.

About LESS:

Less Framework is a CSS grid system for designing responsive WebUI's either its for a mobile or laptop. It contains 4 layouts, 1) default layuout 2)Mobile Layout

3)Tablet Layout 4)Mobile with Extensive layout.

Depends on your requirement you can use any of the above 4 layouts with respect to Columns.It works corresponding grids with pixel sizes.

The best deal is to first code the Default Layout (992 px), and then use CSS3 media queries to code several child layouts: 768, 480, and 320 px. The Default Layout will be served to any browsers that do not support media queries, whereas the child layouts will be served, as appropriate, to browsers that do. They will also inherit all styles given to the Default Layout, so coding them is very fast.

 

you can find more about LESS:  Getting started | Less.js

 

here is a simple example for login page.

 

Step 1: Add CSS file in your index.html of SAPUI5 project. Add the test.css after </script> tag for best practice.

 

   </script>

    <link rel="stylesheet" href="../css/less/test.css" />

  </head>

  <body class="sapUiBody" role="application">

    <div id="content"></div>

 

Step 2: I have defined the content in my login.less file.

 

.login-username-label,

.login-password-label {

  display: none;

  font-size: @font-size-default;

  font-family: @font-ericsson-arial;

  margin: 0 0 -5px 0;

  padding: 10px 0 0 0;

  text-align: left;

}

 

Step 3:  In test.less you have to import the required LESS files with respect to your project.

 

@import "menu.less";

@import "login.less";

 

Step 4: you can write your batch file to create the CSS files. either single or multiple CSS. so here i am creating test.css instead of menu.css,login.css.

Hence according to my login.less, test.css has created.

 

 

test.JPG

Step 5: So once you deploy your SAPUI5/HTML5 application either in mobile or desktop, the view will be like this.

Desktop view:

login_desk.png

Mobile View:

login_mobile.png

@ Experts: let me know if anything need to be added.

 

Regards,

Prasad Sistla.

Accessing HANA XS OData in Web IDE and beyond

$
0
0

Hello everybody,

 

here I'd like to share what I have learned while trying to access a HANA XS OData service in our SAP Web IDE (cloud) trial edition. I've have been asked by several people if it is possible - and the answer is: it depends.

You could also access the services with a node.js solution by Gregor Wolf!

 

With this blog post I don't want to start a discussion about "why is it not possible under certain circumstances?" - because there are lots of technical internal details involved. I've talked with a bunch of people and addressed the actual circumstances there.

 

Here I just want to highlight what you can do TODAY to give it a try. This is not a detailed step by step tutorial here, I just give you hints what you can do with certain examples, because the whole HCP/HANA topic is quite big and there are a lot of resources out there:


So if you have any questions concerning the HANA (privileges, shine app, sflight...) / HCP (destinations...) topics highlighted here in this blog post I'd kindly ask you to go to the corresponding SNC spaces for HANAand HCP or have a look into the openSAPcourses to learn more.

 

If you are new to the SAP Web IDE visit our SCN space with lots of tutorials.

 

 

SAP Web IDE and SAP HANA Trial: not yet

 

I've been in discussions with Eduard Bartsch and Martin Raepple about this topic and currently it is not possible to consume the HANA XS OData services via the HCP cloud destinations in our SAP Web IDE.

 

It also has been asked if you could change the settings in the of the .xsaccess file to Basic or anonymous -> no it is not possible, these settings are ignored, SAML2 only.

 

So this means this setup is not possible yet:

hanatrial.png

 

SAP Web IDE and SAP HANA via SAP CAL: yes

 

Last week I got great help from Craig Cmehil in setting up my SAP HANA system via the SAP CAL (Cloud Appliance Library) - this is basically a system hosted on AWS where you just pay for the time the machine is running. The AWS setup is also used in the openSAP courses at the beginning - later they are also using the SAP HANA Web based development environment. SAP CAL is just an super easy user interface for controlling your AWS machine.

 

Here is the info about the AWS with the "Getting started guide" and here is the info about the included SAP HANA.

 

I have used the SFlight service and used the Basic Authentication which was available by default with my database user. Here the destination settings in the HCP cockpit - the URL points to the IP-address of my AWS machine:

sflight.png

Working in SAP Web IDE with SAP HANA by SAP CAL:

hana_flight_running.PNG

 

Mock in SAP Web IDE and "importing" into SAP HANA trial Web IDE: yes

 

This is maybe not the most elegant solution, but you can leverage our SAP Web IDE for creating the SAPUI5 application with our SAP Fiori template, code completion, ... and use the mock data service to preview the app without the working connection. After that you could put this app into your SAP HANA trial account, make some adaptions and continue the development there.

 

For this I've used the sample SHINE application from SAP:

 

poworklist.png

I executed the service and saved the response from $metadata as metadata.xml:

metadata.png

In the SAP Web IDE I created a project based on the SAP Fiori Starter Applications and uploaded the metadata.xml from my local pc:

metadataxml_in_webide.png

 

To test the application you have to run it with mock data:

run_with_mock_data.png

Mocked data:

mocked_data.png

 

After that I have downloaded the project as zip (and then unpacked it):

export_project.png

 

Then I switched over to the SAP HANA Web IDE:

launch_hana_web_ide.png

In my shine application I created the corresponding packages and drag & dropped the files in the corresponding packages (no dragging of folders possible and no automatic zip-extraction possible yet):

packages_web_ide.png

 

Here are the additional steps I had to do:

 

index.html

I had to change the location of the sapui-core.js:

 

<script id="sap-ui-bootstrap"
src="https://sapui5.hana.ondemand.com/resources/sap-ui-core.js"

The one provided from the shine application (/sap/ui5/1/resources/sap-ui-core.js) had problems with the Icon Tab Bar.

 

Component.js

I had to reference the location of the XS service:

 

var sServiceUrl = "../../services/poWorklist.xsodata/";

Additional pitfalls

During development sometimes I had to run the application again so that the source was updated:

run_on_server.png

And sometimes I even had to clear the cache:

empty_cache.png

 

Here the final running XS application:

hana_xs_running.png

Maybe the steps with the file replacement are easier within your local Eclipse and the HANA tools connected to your trial cloud system, I haven't tried it out yet - I must continue with my openSAP course ;-) I will update this section after that.

 

HANA XS trial and beyond

 

SAP Mentor Gregor Wolf also played around with the SAP HANA trial XS services and came up with a node.js solution which he hosts here on github.

 

TL;DR

  • HANA trial consuming in SAP Web IDE via HCP destination service: not yet
    .xsaccess settings could not be set to Basic or anonymous -> service is always SAML2 based due to trial restrictions
  • HANA productive or HANA on AWS through SAP CAL via HCP destination service consuming in SAP Web IDE: yes --> AWS link
    (I've seen also CORS settings in the XS seetings so maybe this is even possible without HCP destination service)
  • Mocking in SAP Web IDE and "importing" into SAP HANA Web IDE: yes
  • Accessing service via node.js: yes

 

Summary

 

I hope I could have given you some hints on how you can consume the SAP HANA XS services in the SAP Web IDE or even with node.js - thanks to Gregor Wolf. If you are taking the openSAP about software development on SAP HANA you would probably already have your AWS machine up and running. The SAP HANA trial is at the moment ( ! )  from my understanding an isolated system where you can develop your XS applications, but you normally can not consume the services from the outside or via the HCP destination service (without destination service you could not consume them in your browser because they are not CORS enabled).

UI5 TDG Demo - CORS Error - Alternative Solution

$
0
0

Problem:


You are working on TDG Demo provided in UI5 developer guide. Your application fails to load data from sample OData service due to web security / CORS Cross-origin resource sharing (CORS).

 

As per sap help documentation , you should over come this issue by using simple proxy servlet with additional context param as mentioned at this link. But, sometime this may not work due to some unknown reasons.

 

Below is the alternative solution to overcome this issue.

 

 

Errors:

 

XMLHttpRequest cannot load http://domain2.example. Origin http://domain1.example is not allowed by Access-Control-Allow-Origin.

 

HTTP Status 500 - Server redirected too many times (20)



Solution: Use CORSPROXY


Steps


1. Install CORSPROXY by giving below command at command or $ prompt

npm install -g corsproxy


2. Start CORSPROXY by giving below command at command prompt

$ corsproxy


3. This starts corsproxy in it's default port 9292. You can launch it by http://localhost:9292


4. Now, you can use your ODATA service url by appending it to corsproxy url

     ie.

            http://services.odata.org/V2/(S(sapuidemotdg))/OData/OData.svc  


     will be called by below url

            

           http://localhost:9292/services.odata.org/V2/(S(sapuidemotdg))/OData/OData.svc

    

      You can test it by launching directly.

 

5. Now use this new url in component.js. Run UI5 TDG Demo app and you should see list of products from odata service.



References:


https://www.npmjs.com/package/corsproxy

Views navigation in SAP UI5 for Desktop applications

$
0
0

This blog explains how to implement navigations between UI5 (desktop) views. There are few other ways to implement the navigation but I feel this is much more simple and easy way. I am gonna focus on what need to be done exactly rather than explaining each and every step from the scratch. So let’s get it started!!

 

Prerequisites

  1. Eclipse IDE
  2. UI5 Tool Kit installed
  3. Google Chrome

 

Steps

  1. Create a new SAP UI5 project in Eclipse (Desktop type)
  2. Create three Javascript views (MainView, FirstView, SecondView).


IMP NOTE : While creating unique ids for the UI elements use this.createId(“”) method since this will create unique ids dynamically and to access any ui element with its unique id in its controller, then make sure that you pass the controller reference while calling a function (Ex: { press : [oController.function,oController] } and you can access the ui element properties by this.byId("unique_id").getValue())

 

    Index.html

     index.png

 

   MainView.view.js

     mainview_ui.png

mainview_js.png

 

    FirstView.view.js

  

        firstview_ui.png

        firstview_js.png

 

 

SecondView.view.js


          secondview_ui.png

          secondview_js.png


  Now create functions in the controllers as below


  MainView.controller.js

           maincontroller.png

    

  1. loadHomePage()    - which loads content of MainView (Panel content) by removing the existing content of MainPanel and the adding the MainView’s content (Panel)
  2. loadFirstPage()       - which loads content of FirstView  (Panel1 content) by removing the existing content of MainPanel and then adding the FirstView’s content (Panel1)
  3. loadSecondPage()– which loads content of SecondView (Panel2 content) by removing the existing content of MainPanel and then adding the SecondView’s content (Panel2)


Here the logic is simple, the above three functions in MainView controller will remove the current content of MainPanel  and will keep adding the next screens content dynamically. And these functions are called on every button press to navigate to the other view


   FirstView.controller.js

            firstviewcontroller.png

  1. homePage()    - calls loadHomePage() function of MainView controller and this is called when Go to Home Page button is clicked
  2. secondPage()– calls loadSecondPage() function of MainView controller and this is called when Go to Second Page button is clicked


    SecondView.controller.js

            secondviewcontroller.png

  1. homePage()    - calls loadHomePage() function of MainView controller and this is called when Go to Home Page button is clicked
  2. firstPage()     –  calls loadFirstPage() function of MainView controller and this is called when Go to First Page button is clicked

 

 

This is how it works

          overview.jpg

     That’s it!! If everything is in place then it’s time to run the application

 

     Happy Coding

     Cheers!

     Samba

Changing a BSP Application into SAPUI5 layout by changing one line - the idea for a new BSP Extension

$
0
0

A lot of customers are still using BSP (Business Server Pages) Applications and lots of them are written with BSP Extensions. With BSP Extensions you don't have to take care about Browser dependent programming or different layout options. The offered BSP Extensions from SAP take care about this. Here an easy "Hello World" page written with BSP Extensions:

BSPEXT01.jpg

 

The result of this code is:

 

BSPEXT02.jpg

In this example the Extension library "htmlb" is used. If a BSP Extension is able to generate HTML code, why not SAPUI5 HTML code? And the great thing is, that the Extensions are open to develop own Extensions...

 

This seems to be a great opportunity!

 

I made a test and created a new BSP-Extension "zui5" and implemented the elements from the example above. I used SAPUI5 Declarative Programming for this.

 

BSPEXT03.jpg

With this new Extension only one line has to be changed.

BSPEXT04.jpg

And the result is a SAPUI5 page, at least it looks like a SAPUI5 page:

BSPEXT05.jpg

 

OK, you are right, this page is not that impressive, but is shows that the idea works.

 

The idea is to have a SAPUI5 BSP Extension that can change the layout of every BSP Application written with BSP Extensions only by changing one line.

 

I know, this does not make a real SAPUI5 Application out of the already existing Applications. Things like JSON Data access and lots of others possibility are not taking into account. I don't see this as a new environment for developing new SAPUI5 applications. But if such an Extension give the opportunity to change the layout of already existing Applications, this could be great.

 

I will implement more elements and do more test and let you know the result of this. Meanwhile I would love to get your feedback for this approach.

Colorful App Tiles in Fiori Launchpad

$
0
0

Let's create app tiles with random colors on the Fiori launchpad. We usually see a tile with a single color either grey or other color in the Fiori launchpad. This blog will show you how to create a colorful tiles on the launchpad like in the below images.

2.jpg

 

3.jpg

 

 

In order to accomplish, we need to modify the FioriLaunchpad.html and add the background color attribute in class sapUshellTileInner.

11.jpg

 

 

Modify FioriLaunchpad.html

 

Create a copy of your FioriLaunchpad.html as a backup and modify the old one.

10.jpg

Add the following codes right before function main():

//Custom Codes to Refresh Page Starts Here

  function goToURL(){

              location.href='https://<Netweaver_Gateway_Server_Address>/fiori/shells/abap/Fiorilaunchpad.html';

            }

  //Custom Codes to Refresh Page Ends Here

This function is required to reload the FioriLaunchpad page when user press the "Home" button.

6.jpg

Search for the following codes in FioriLaunchpad.html:

sap.ui.getCore().getEventBus().subscribe("launchpad", "contentRendered",

                    function () {

                        //this setTimeout is MANDATORY to make this call an async one!

                        setTimeout(function () {

                            try {

                                jQuery.sap.require('sap.fiori.core-ext');

 

And add the below codes:

var colors = ["#FFCDD2","#F8BBD0","#E1BEE7","#D1C4E9","#BBDEFB","#B3E5FC","#B2DFDB","#A5D6A7","#E6EE9C","#FFF59D","#FFCC80"];

                                var cols = document.getElementsByClassName('sapUshellTileInner');

                                for(i=0; i<cols.length; i++) {

                                      var bgcolor = Math.floor(Math.random() * colors.length);

                                      var selectedcolor = colors[bgcolor];

                                      cols[i].style.backgroundColor =  selectedcolor;

                                  }

 

It will add the random background colors to the sapUshellTileInner class.

Launch tile

The modified codes should now look like this:

5.jpg

 

Ok, we are done with FioriLaunchpad. Let's try to refresh the page. You should see the colorful tiles. The next step that we need to do is to add JavaScript codes to the index.htmlof Fiori app that will be called by the Fiori Launchpad.

 

Index.html of Extended Fiori App

 

We will modify the index.html of the extended Fiori app "Create Sales Order".

 

7.jpg

 

Add the following codes as highlighted in red box as per below screenshot.

8.jpg

 

The first section is to modify the homeBtn tag and add the JavaScript function to load the Fiori Launchpad main page.

12.jpg

The second section is to prevent a user to click the back button. The only way to go back to the Fiori Launchpad page is to click on the "Home" button.

9.jpg

Create the defer.js in the root folder to remove the tag button: __button0  from the child frame of the Fiori app.

var element = document.getElementById("__button0");

 

if(element!=null) {

  $('button[id^="__button0"]').remove();

}

 

That's all. Now you should have a rainbow Fiori Launchpad. I have attached the modified FioriLaunchpad.html, defer.js and index.html for your reference.

 

Thanks for reading my blog. If you have any questions/comments, please let me know.

Colorful App Tiles in Fiori Launchpad (Enhanced version)

$
0
0

I just found out an easy way to update the app tiles background color in Fiori Launchpad main page. The earlier version that I wrote in this blog requires a reloading of the Fiori Launchpad page. With this enhanced version, the reloading of the Fiori Launchpad page and the modification of the index.html of the Fiori app are not required.

 

13.jpg

 

We need to create a simple thread that keep monitoring the background color attribute of sapUshellTileInner tag. If the background color attribute is not presence, it will add the attribute.

 

Add the following codes within the head tag of FioriLaunchpad.html:

 

<script>  //Custom Codes to create a colourful Tiles Starts Here  function timeout() {  setTimeout(function () {  var cols = document.getElementsByClassName('sapUshellTileInner');  if (cols!=null) {  for(i=0; i<cols.length; i++) {  iscolor = cols[i].style.backgroundColor;  if (iscolor=="") {  var colors = ["#FFCDD2","#F8BBD0","#E1BEE7","#D1C4E9","#BBDEFB","#B3E5FC","#B2DFDB","#A5D6A7","#E6EE9C","#FFF59D","#FFCC80"];  var bodybgarrayno = Math.floor(Math.random() * colors.length);  var selectedcolor = colors[bodybgarrayno];  cols[i].style.backgroundColor =  selectedcolor;  }                        }  }  timeout();  }, 1000);  }  //Custom Codes to create a colourful Tiles Ends Here  </script>

 

And update the html body tag with this:

<body onload="timeout();" class="sapUiBody sapUShellFullHeight" role="application">

 

Refresh the page. That's all about it.

 

Please find the enhanced modification of FioriLaunchpad.html in the attachment.

 

Thanks for reading my blog and let me know if you have any comments.


Fiori Launchpad with Random Flowers

$
0
0

Some thoughts that I would like to share how to enhance the look and feel of the Fiori Launchpad: "Fiori Launchpad with random Flowers". Basically we will cover the original layer with the custom layer that we will build in this blog by modifying the FioriLaunchpad.html. The modification is not perfect, but at least we know the concept and we can enhance further.

 

aa.jpg

 

Concept

 

Let see what is the html tag behind the standard tile in Fiori Launchpad.

d.jpg

Below is the result, we will replace the child tags under the class sapUshellTileInner with our HTML tags.

c.jpg

Let's create a template tpl.html to replace that section.

e.jpg
We will replace the following values:

Value to be replacedReplace With
_link_URL to the Fiori app
_title1_Title of the Fiori app
_title2_Sub-title of Fiori app
_image_Fiori Flower random image

 

The final output will be like this

f.jpg

 

We will not replacing a News tile with our custom HTML tag. The News tile will appear as it is. All this logic will be defined in the JavaScript codes.

 

Let's put all together the concept in the JavaScript codes in FioriLaunchpad.html:

 

function() {                    //this setTimeout is MANDATORY to make this call an async one!                    setTimeout(function() {                        try {                            jQuery.sap.require('sap.fiori.core-ext');                            //Custom Codes to create a colourful Tiles Starts Here                            var xhr = new XMLHttpRequest();                            var tpl, tplr, href, y, len, img, bodybgarrayno, selectedimg, nt;                            var arr = new Array();                            var fioriimg = ["fiori1", "fiori2", "fiori3", "fiori4", "fiori5"];                            xhr.onreadystatechange = function() {                                if (xhr.readyState === 4) {                                    var els = document.getElementsByClassName('sapUshellTileInner');                                    for (i = 0; i < els.length; i++) {                                        nt = els[i].innerHTML.search("News Tile");                                        if (nt == -1) {                                            if (els[i].innerHTML.match(/href="([^"]*)/) != null)                                                href = els[i].innerHTML.match(/href="([^"]*)/)[1];                                            else                                                href = "";                                            tpl = xhr.responseText;                                            tplr = tpl.replace("_link_", href)                                            y = 0;                                            bodybgarrayno = Math.floor(Math.random() * fioriimg.length);                                            selectedimg = fioriimg[bodybgarrayno];                                            tplr = tplr.replace("_image_", "https://sapnetweaverserver/fiori/" + selectedimg + ".png")                                            len = els[i].innerText.trim().split("\n").length;                                            $.each(els[i].innerText.trim().split("\n"), function(index, element) {                                                //console.log(element.trim() + "#" + index + "#" + element.trim().length);                                                if (element.trim().length > 0) {                                                    if (element.trim().length > 1) {                                                        if (y == 0) {                                                            tplr = tplr.replace("_title1_", element);                                                        }                                                    }                                                    if (element.trim().length >= 1 && len < 2) {                                                        if (y == 0) {                                                            tplr = tplr.replace("_title1_", element);                                                            tplr = tplr.replace("_title2_", "");                                                        }                                                    }                                                    if (element.trim().length >= 1) {                                                        if (y == 1) {                                                            tplr = tplr.replace("_title2_", element);                                                        }                                                    }                                                    y = y + 1;                                                }                                            });                                            els[i].innerHTML = tplr;                                        }                                    }                                }                            };                            xhr.open('GET', 'tpl.html');                            xhr.send();                            //Custom Codes to create a colourful Tiles Ends Here                        } catch (error) {                            jQuery.sap.log.warning("failed to load sap.fiori.core-ext!");                        }                    }, 0);                });


Also update the html body tag:

<body onload="timeout();" class="sapUiBody sapUShellFullHeight" role="application">

 

//Custom Codes to create a colourful Tiles Starts Here        function timeout() {                setTimeout(function() {                    //Custom Codes to create a colourful Tiles Starts Here                    var cols = document.getElementsByClassName('sapUshellTileInner');                    if (cols != null) {                        //Custom Codes to create a colourful Tiles Starts Here                        var xhr = new XMLHttpRequest();                        var tpl, tplr, href, y, len, img, bodybgarrayno, selectedimg, nt, iscolor;                        var arr = new Array();                        var fioriimg = ["fiori1", "fiori2", "fiori3", "fiori4", "fiori5"];                        for (i = 0; i < cols.length; i++) {                            iscolor = cols[i].style.backgroundColor;                            if (iscolor == "") {                                cols[i].style.backgroundColor = "#FFFFFF";                                xhr.onreadystatechange = function() {                                    if (xhr.readyState === 4) {                                        var els = document.getElementsByClassName('sapUshellTileInner');                                        for (i = 0; i < els.length; i++) {                                            nt = els[i].innerHTML.search("News Tile");                                            if (nt == -1) {                                                if (els[i].innerHTML.match(/href="([^"]*)/) != null)                                                    href = els[i].innerHTML.match(/href="([^"]*)/)[1];                                                else                                                    href = "";                                                tpl = xhr.responseText;                                                tplr = tpl.replace("_link_", href)                                                y = 0;                                                bodybgarrayno = Math.floor(Math.random() * fioriimg.length);                                                selectedimg = fioriimg[bodybgarrayno];                                                tplr = tplr.replace("_image_", "https://sapnetweaverserver/fiori/" + selectedimg + ".png")                                                len = els[i].innerText.trim().split("\n").length;                                                $.each(els[i].innerText.trim().split("\n"), function(index, element) {                                                    //console.log(element.trim() + "#" + index + "#" + element.trim().length);                                                    if (element.trim().length > 0) {                                                        if (element.trim().length > 1) {                                                            if (y == 0) {                                                                tplr = tplr.replace("_title1_", element);                                                            }                                                        }                                                        if (element.trim().length >= 1 && len < 2) {                                                            if (y == 0) {                                                                tplr = tplr.replace("_title1_", element);                                                                tplr = tplr.replace("_title2_", "");                                                            }                                                        }                                                        if (element.trim().length >= 1) {                                                            if (y == 1) {                                                                tplr = tplr.replace("_title2_", element);                                                            }                                                        }                                                        y = y + 1;                                                    }                                                });                                                els[i].innerHTML = tplr;                                            }                                        }                                    }                                };                                xhr.open('GET', 'tpl.html');                                xhr.send();                            }                        }                        //Custom Codes to create a colourful Tiles Ends Here                    }                    timeout();                }, 1000);            }            //Custom Codes to create a colourful Tiles Ends Here    </script>


Upload the Supporting Files to Netweaver Gateway


All supporting documents and modified FioriLaunchpad.html can be found in the attachment.

  • Upload the template file tpl.html that we have created earlier.

k.jpg

 

 

  • Create and upload a custom CSS file: sap.css.
    .sapSuiteUiCommonsFeedTileLowerText1 {
    margin-top: 6px;
    font-size: .875rem;
    font-family: 'OpenSans-Regular', sans-serif;
    text-align: center;
    color: #cccccc;
    width: 100%;
    }
    .sapSuiteUiCommonsFeedTileAge1 {
    width: 100%;
    }
    .sapSuiteUiCommonsFeedTileTitle1 {
    font-family: 'OpenSans-Semibold', sans-serif;
    color: white;
    font-size: 1.125rem;
    padding: 8px 15px 0px 15px;
    word-wrap: break-word;
    overflow: hidden;
    height: 53%;
    }
    i.jpg

 

  • Upload the 5 flower images. The images will be displayed randomly.

g.jpg

 

We are done with the modification. You can try run and see it in Chrome or IE.

 

Here is the final look:

 

Thanks for reading my blog.

 

References:

Disable HTTPS Security Certificate in ( MOBILE/TABLET) - Android and IOS

$
0
0

Hi All,

 

This blog tells about how to disable trusted certificate path in mobile/tablet.

 

What is HTTPS protocol:


                                               Hypertext Transfer Protocol Secure (HTTPS) is a communications protocol for secure communication over a computer network, with especially wide deployment on the Internet. Technically, it is not a protocol in and of itself; rather, it is the result of simplylayering the Hypertext Transfer Protocol (HTTP) on top of the SSL/TLS protocol, thus adding the security capabilities of SSL/TLS to standard HTTP communications. The main motivation for HTTPS is to prevent wiretapping and man-in-the-middle attacks.


If you dont have certificate path. This may cause problem to download file, image, videos from HTTPS secure connection.


Solution:


By Using File transfer plugin, we can avoid this problem.


Steps:


var downloadUrl = "https://YOURHOSTNAME/ SAPPATH /File('MYPDFFILE')/$value";

 

fileTransfer.download(

  downloadUrl,

  fileSystem.root.toURL() + '/' + relativeFilePath,

  function (entry) {

  var finalRes = fileSystem.root.toURL() + '/' + relativeFilePath;

  //Success

  },

  function (error) {

  alert("Error during download. Code = " + error.code);

  },

 

true// MAKE THIS AS TRUE ( THIS IS THE FLAG TO DOWNLOAD FILES FROM HTTPS SECURE CONNECTION)

  );



https://github.com/apache/cordova-plugin-file-transfer/blob/master/doc/index.md#download

Go to this link, download this plugin to your Android/Cordova CLI project.



This makes you very  easy to download your file from Secure connection.


Thanks,

Karthik A

Let that flower bloom for you (Using Fiori loading dialog in custom application)

$
0
0

Progress dialog is important part of SAPUI5 application. It prevents user from performing an action while work is in progress and actually shows when action is done.

 

There is a sap.m.BusyDialog control with ability to configure a dialog with custom icon etc. Works perfectly for standalone applications. But when application is embedded into Fiori Launchpad it would be great to have consistent user experience.


Here is a little how to use Fiori loading dialog in custom application. There is a control with a global ID - "loadingDialog"


loadingDialogDOM.PNG

 

So, we can get that flower blossom ;-) With a little help of my old friends - Fiddler and Chrome Developer Tools I figured out that "loadingDialog" is of type sap.ushell.ui.launchpad.LoadingDialog which extends sap.m.BusyDialog.

 

sap.m.BusyDialog.extend("sap.ushell.ui.launchpad.LoadingDialog"...

 

Here is a first trick: you would expect it to implement the same set of open/close methods as a parent, but it actually implements another set: openLoadingScreen and closeLoadingScreen

Second trick: keep a reference to busy dialog in one place and control it with events.

 

here is some code (code be controller, view or component):

 

//Init
this.oLoadingDialog = sap.ui.getCore().byId("loadingDialog");
if(!this.oLoadingDialog)
{     this.oLoadingDialog = new sap.m.BusyDialog( {          customIcon : "img/progress.gif",          customIconWidth : "10px",          customIconHeight : "10px"     });
}
.......
...getEventBus().subscribe("App", "StartProgressDialog",  this.startProgressDialog, this);
...getEventBus().subscribe("App", "StopProgressDialog",   this.stopProgressDialog, this);
.......
stopProgressDialog : function()
{     if(jQuery.sap.isDeclared("sap.ushell.ui.launchpad.LoadingDialog")) //or if(sap.ui.getCore().byId("loadingDialog"))     {          this.oLoadingDialog.closeLoadingScreen();     }     else     {     this.oLoadingDialog.close();     }
},
startProgressDialog : function()
{     if(jQuery.sap.isDeclared("sap.ushell.ui.launchpad.LoadingDialog")) //or if(sap.ui.getCore().byId("loadingDialog"))     {          this.oLoadingDialog.openLoadingScreen();     }     else     {          this.oLoadingDialog.open();     }
},

To start a loading dialog we just need to fire an event:

 

....getEventBus().publish("App", "StartProgressDialog", null);

and

 

....getEventBus().publish("App", "StopProgressDialog", null);

to stop it.

Tristate sap.m.CheckBox

$
0
0

Just want to share a Tristate Checkbox in for SAP Mobile control.

 

It illustrates the followings

1. Extending an existing control. sap.m.Checkbox

2.Using css :before to place icon in the checkbox

3. Override the fireSelect function so that the state is returned to the listener.

4. With a few lines of code and CSS, we can easily extend an control

 

sap.m.CheckBox.extend('TriStateCheckBox', {  metadata: {    properties: {      state: {type: 'int', defaultValue: 0}    }  },    onAfterRendering: function() {    var $this = this.$();    $this.find('div').removeClass('sapMCbMarkChecked');    $this.removeClass('triStateCheckBoxSelected');    $this.removeClass('triStateCheckBoxMixed');        if (this.getState() === 1) {       $this.addClass('triStateCheckBoxSelected');    } else if (this.getState() === 2) {      $this.addClass('triStateCheckBoxMixed');    }  },    fireSelect: function(s) {    var v = s.selected ? 1 : 0;    this.setState(v);    this.fireEvent('select', {'state': v});  },  renderer: {}
});

CSS

.triStateCheckBoxSelected div:before {   content: "\e05b";  font-family: "SAP-icons";  color: #007cc0; 
}

.triStateCheckBoxMixed div:before {
   content: "\e17b";  font-family: "SAP-icons";  color: #007cc0; 
}

 

-Dennis

Offline Storage | SAPUI5 | Using IndexedDB & JQuery API

$
0
0

Background

There is always a need to store data within any Web or Mobile application for handling offline scenarios or for processing data when there is no network connectivity. I had done a small POC for a SAPUI5 based application and would like to share with you community members. You can post your comments or suggestions to add more value to this blog.

 

Purpose

This blog illustrates the implementation of offline storage of data from an OData Service in a SAPUI5 Application designed for Web and Mobile.

IndexedDB & SAPUI5 JQuery Storage are the two common techniques used for the implementation.

 

Scope

We will be creating a simple SAPUI5 application integrating a SAP OData Service and try to store the retrieved data within the browser using IndexedDB & Local Storage.

 

OData Service

I have created an OData Service which will get records from the backend table. The structure of the collection which we will be using is as follows;

OData Service.png

 

   where, Enitity is "Employee"

                 EntitySet is "EmployeeCollection"

 

And, this entity will store details of an employee like ECODE, FIRSTNAME, LASTNAME, DEPTCODE and DEPTDESC.

We will now consume this service in our SAPUI5 application.


Creating View and OData Model

 

We will create a SAPUI5 application with a single view which will contain two tables, one to display data from OData Service & another to display data from offline storage.

SAPUI5 View.png

We will be storing the data retrieved from our OData service into our offline store & later display the same from the offline store into our view.

 

Let's create a sap.m.Table with 5 columns (as we have 5 properties in our collection) in createContent function of our view.js.

 

var emplTbl = new sap.m.Table("id_empTbl", {

 

width : '600px',

columns: [

           new sap.m.Column({

           header: new sap.m.Text({

           text: "Emp Code"

           })

               }),

 

           new sap.m.Column({

           header: new sap.m.Text({

           text: "First Name"

           })

               }),

 

           new sap.m.Column({

           header: new sap.m.Text({

           text: "Last Name",

           })

               }),

 

           new sap.m.Column({

           header: new sap.m.Text({

           text: "Dept Code"

           })

               }),

 

           new sap.m.Column({

           header: new sap.m.Text({

           text: "Description"

           })

               }),

 

   ],

        

      items : {

      path: '/EmployeeCollection',

      template: new sap.m.ColumnListItem({

      selected: true,

      type: "Active",

      cells: [

                 new sap.m.Text({

                 text: "{ECODE}"

                     }),

 

 

                new sap.m.Text({

                text: "{FIRSTNAME}"

                     }),

 

 

                new sap.m.Text({

                 text: "{LASTNAME}"

                     }),

 

 

                new sap.m.Text({

                 text: "{DEPTCODE}"

                     }),

 

 

                new sap.m.Text({

                 text: "{DEPTDESC}"

                     })]

                  })

          }});

 

 

Let's create another sap.m.Table to display data from offline store.

 

var emplOfflineTbl = new sap.m.Table("emplOfflineTbl", {

 

               width : '600px',

               columns: [

                         new sap.m.Column({

                          header: new sap.m.Text({

                          text: "Emp Code"

                               })

                              }),

 

                         new sap.m.Column({

                          header: new sap.m.Text({

                          text: "First Name"

                               })

                              }),

 

                         new sap.m.Column({

                          header: new sap.m.Text({

                          text: "Last Name",

                               })

                              }),

 

                         new sap.m.Column({

                          header: new sap.m.Text({

                          text: "Dept Code"

                               })

                              }),

 

                         new sap.m.Column({

                          header: new sap.m.Text({

                          text: "Description"

                               })

                              }),

 

                              ],

                    });

 

Let's put the both the tables in a layout.

 

var oLayout = new sap.ui.layout.HorizontalLayout("horizontalLayout", {

               width: "100%",

               content: [emplTbl,emplOfflineTbl]

     });

 

Two buttons at the top. The press event of the buttons refer to methods in controller.js, which we will be coding later in this section.

 

var bar = new sap.m.Bar("bar", {

          contentLeft : [new sap.m.Label("label", {

     text: "Displaying On-line data from OData Service"

               })],

 

          contentRight: [ new sap.m.Button("storeOffline",

                         {text: "Store data offline",

          press: oController.writeToIDB}),

 

       new sap.m.Button("showOffline",

          {text: "Show offline data",

                              press: oController.showOfflineData }),

 

]

});

 

 

 

Finally let's return our content in a page.

 

          return new sap.m.Page({

               title: "Employee Records",

               content: [bar,oLayout]

               });

 

Let's create an OData Model to integrate our OData service and bind it to the UI control (first table).

We will modify the onInit function of our controller.js.

 

onInit: function() {

serviceUrl = "http://<host>:<port>/sap/opu/odata/sap/<external_service_name>";

oModel = new sap.ui.model.odata.ODataModel(serviceUrl,false)

 

},

 

Bind the model to your first sap.m.Table. Modify the onBeforeRendering function in your controller.js.

 

onBeforeRendering: function() {

          sap.ui.getCore().byId("id_empTbl").setModel(oModel);

               },

 

Let's run our project and see the output;


Output1.png

 

Records from OData Service are displayed in our view.

 

We will be storing these records in an IndexedDB within our browser.

 

IndexedDB

 

IndexedDB allows you to create a client-side persistent storage to store large amount of data within the browser.

It helps in storing the data in structured way thus enhancing the browser based app's capability to not only store data for offline usage but also query the data more efficiently.

 

Step1: Let's check if our browser supports IndexedDB.

 

if(window.indexedDB == null)

{

               console.error("Offline store not supported!");

               return null;

}

 

Step2: If supported, let's create a database.

 

else{

               var createDBRequest= window.indexedDB.open("MyOfflineDB", 1);

     }

 

where, "MyOfflineDB" is the database name and "1" is the database version

 

Step3: Let's add few events to our request.

 

     createDBRequest.onupgradeneeded = function(event){

                         var db = event.target.result;

                                           };

 

     createDBRequest.onsuccess = function(event){

                    oController.myDB = event.target.result; //oController - which we will define in onInit function

          };

 

     createDBRequest.onerror = function(oError)

     {

alert("Something went wrong!");

     };

 

Event onupgradeneeded will be called whenever a new database is created or an existing database with higher version is created.

Event onsucess will be called on successful opening of the database.

Event onerror will be called if anything goes wrong with our request.

 

 

 

Step4: Create an Object Store to hold the data.

 

In simple terms an Object Store is nothing but a Table with one or more fields. It helps in structuring our data.
An Object Store will hold the data in a key-value pair & will store the data according to keys in ascending order.

 

We will do this in our onupgradeneeded  event.

 

createDBRequest.onupgradeneeded = function(event){

var db = event.target.result;

 

var objectStore = db.createObjectStore("EmployeeStore", { keyPath: "ECODE" });

          objectStore.createIndex("ECODE", "ECODE", { unique: true });

          objectStore.createIndex("FIRSTNAME", "FIRSTNAME", { unique: false });

          objectStore.createIndex("LASTNAME", "LASTNAME", { unique: false });

          objectStore.createIndex("DEPTCODE", "DEPTCODE", { unique: false });

          objectStore.createIndex("DEPTDESC", "DEPTDESC", { unique: false });

 

     };

 

So here I have created an Object Store which will hold the Properties of my Collection of my OData Service.

In my Object Store ECODE (Employee Code) is the key.

 

Step5: Once we have created our Database and Object Store to store our data from an OData Service, lets code to write the data to the Object Store.

 

Remember, we had created OData Model in our onInit function of controller.js.

 

We will get all the objects from that model.

var oObjects = oModel.oData;

 

Then we will loop at these objects and store one-by-one in the Object Store.

for (var key in oObjects) {

                    if (oObjects.hasOwnProperty(key)) {

var oObject = oObjects[key];

var oRecord = {ECODE: oObject.ECODE, FIRSTNAME: oObject.FIRSTNAME, LASTNAME: oObject.LASTNAME ,DEPTCODE: oObject.DEPTCODE, DEPTDESC: oObject.DEPTDESC  };

 

var oTransaction = oController.myDB.transaction(["EmployeeStore"], "readwrite");

var oDataStore = oTransaction.objectStore("EmployeeStore");

                              oDataStore.add(oRecord);

                         }

 

Step6: Let's retrieve the stored data

 

There are a couple of ways to retrieve the data in Object Store.

1. Using objectStore.get("key") method wherein we can fetch a single record using its key

2. Using a iterator - cusror, to iterate over multiple records

 

var objectStore = oController.myDB.transaction("EmployeeStore").objectStore("EmployeeStore");

var items = [];

          objectStore.openCursor().onsuccess = function(event) {

          var cursor = event.target.result;

          if (cursor) {

               items.push(cursor.value);

               cursor.continue();

        }

else {

                         alert("Done Processing");

                          // Logic to bind the obtained data in "items" array to your UI control

      }

 

Here, we will access our Object Store, iterate over records using cursor and store it in an array.

The IF condition checks whether there exists any further (next) record or not.

After processing the last record, the control goes into ELSE condition where you can play with the obtained data and your UI control.

 

 

Logic Implementation

 

Let's organize our code in controller.js.

 

We will create our offline database while our application is being initialized.

 

 

Modify your onInit function in controller.js.

 

     onInit: function() {

          serviceUrl = "http://<host>:<port>/sap/opu/odata/sap/<external_service_name>";

          oModel = new sap.ui.model.odata.ODataModel(serviceUrl,false)

 

               oController = this;

               this.prepareIDB();

           },

 

We are calling another method "prepareDB".

 

prepareIDB: function(){

 

if(window.indexedDB == null)

{

               console.error("Offline store not supported!");

               return null;

}

 

else {

          var createDBRequest = window.indexedDB.open("MyOfflineDB", 1);

 

          createDBRequest.onupgradeneeded = function(event){

var db = event.target.result;

 

var objectStore = db.createObjectStore("EmployeeStore", { keyPath: "ECODE" });

                    objectStore.createIndex("ECODE", "ECODE", { unique: true });

                    objectStore.createIndex("FIRSTNAME", "FIRSTNAME", { unique: false });

                    objectStore.createIndex("LASTNAME", "LASTNAME", { unique: false });

                    objectStore.createIndex("DEPTCODE", "DEPTCODE", { unique: false });

                    objectStore.createIndex("DEPTDESC", "DEPTDESC", { unique: false });

               };

 

          createDBRequest.onsuccess = function(event){

               oController.myDB = event.target.result;

          };

 

          createDBRequest.onerror = function(oError)

          {    

                    alert("Something went wrong!");

          };

           }

     },

 

Remember we have declared the press event ("press: oController.writeToIDB") of our button to store data in offline store. Let’s have the method as below;

 

writeToIDB: function(){

var oObjects = oModel.oData;

 

for (var key in oObjects) {

               if (oObjects.hasOwnProperty(key)) {

var oObject = oObjects[key];

var oRecord = {ECODE: oObject.ECODE, FIRSTNAME: oObject.FIRSTNAME, LASTNAME: oObject.LASTNAME ,DEPTCODE: oObject.DEPTCODE, DEPTDESC: "My offline data"  };

 

// altering the last property value, just for display purpose

 

var oTransaction = oController.myDB.transaction(["EmployeeStore"], "readwrite");

var oDataStore = oTransaction.objectStore("EmployeeStore");

                    oDataStore.add(oRecord);

               }

 

          alert("Data stored in db for offline usage.");

          },

 

Similarly, we will code another button's press event method (press: oController.showOfflineData) to get the data from our offline store and display it in the second sap.m.Table at runtime.

 

showOfflineData: function(callback)

{

var objectStore = oController.myDB.transaction("EmployeeStore").objectStore("EmployeeStore");

var items = [];

          objectStore.openCursor().onsuccess = function(event) {

          var cursor = event.target.result;

          if (cursor) {

               items.push(cursor.value);

               cursor.continue();

               }

          else {

alert("Done Processing");

var oJSONModel = new sap.ui.model.json.JSONModel();

                    oJSONModel.setData({modelData: items});

                    sap.ui.getCore().byId("emplOfflineTbl").setModel(oJSONModel);

 

var oTemplate = new sap.m.ColumnListItem( 

{cells: [  

         new sap.m.Text({text : "{ECODE}"}),

         new sap.m.Text({text : "{FIRSTNAME}"}),

         new sap.m.Text({text : "{LASTNAME}"}),

         new sap.m.Text({text : "{DEPTCODE}"}),

         new sap.m.Text({text : "{DEPTDESC}"}),

 

         ] 

});

                    sap.ui.getCore().byId("emplOfflineTbl").bindItems("/modelData",oTemplate);

                }

};

      }

 

Here, we are retrieving the data from our "EmployeeStore" into "items" array. Then creating a JSONModel containing this data & binding it to our second sap.m.Table.

 

 

Test our app

 

Let's run our SAPUI5 application and test our functionality.

 

First Run:

Output2.png

Check resources in your browser:


Output3.png

An IndexedDB "MyOfflineDB" with an Object Store "EmployeeStore" containing required fields is created.

 

 

 

Click on button "Store data offline" and check your IndexedDB:

 

You will see records being stored in the offline store.


Output4.png

Click on button "Show offline data" which will display the data from offline store into our view:


Output5.png


JQuery Storage API's

 

Another simple way of storing data locally within the browser is by using SAPUI5 JQuery Storage.

 

There are 3 possible ways of storing your data using JQuery Storage API's;

  1. jQuery.sap.storage.Type.global  - uses browser's global storage
  2. jQuery.sap.storage.Type.local    - uses browser's local storage
  3. jQuery.sap.storage.Type.session  - uses browser's session storage

 

You may refer below link for more details on JQuery Storage API's;

https://sapui5.hana.ondemand.com/sdk/#docs/api/symbols/jQuery.sap.storage.html

 

 

JQuery Local Storage Implementation

 

Step 1: Initialize JQuery Store

          jQuery.sap.require("jquery.sap.storage"); 

 

Step 2: Define type

     oJQueryStorage = jQuery.sap.storage(jQuery.sap.storage.Type.local); 

 

Step 3: Create JQuery Store & use the "put" method to store the data from OData Model into the

JQuery Store

          oJQueryStorage.put("myJQueryStorage", oModel.oData); 

 

     where, oModel is our OData Model

 

Step 4: Use the "get" method to retrieve the data from your JQuery Store

     var offlineData = oJQueryStorage.get("myJQueryStorage");

 

 

Logic Implementation

 

Let's integrate the JQuery Storage API's in our SAPUI5 application.

 

We will create another button in our first view.

 

 

Output6.png

 

Also, we will create a similar sap.m.Table to display the records from JQuery Store. You may create this table inside the same view. In my case, I have created another view having the below table;

 

var emplOfflineTblJQuery = new sap.m.Table("emplOfflineTblJQuery", {

 

width : '600px',

columns: [

          new sap.m.Column({

header: new sap.m.Text({

text: "Emp Code"

})

          }),

 

          new sap.m.Column({

header: new sap.m.Text({

text: "First Name"

})

          }),

 

          new sap.m.Column({

header: new sap.m.Text({

text: "Last Name",

})

          }),

 

          new sap.m.Column({

header: new sap.m.Text({

text: "Dept Code"

})

          }),

 

          new sap.m.Column({

header: new sap.m.Text({

text: "Description"

})

          }),

 

          ],

});

 

 

Appropriately add the following piece of code in your first view.js (in the top UI bar).

 

new sap.m.Button("showOfflineJQuery",

{text: "Show offline data JQuery",

press: oController.showOfflineDataJQuery})

 

We will write code for the press event (press: oController.showOfflineDataJQuery) of this button ("Show offline data JQuery").

 

Modify your controller.js to add another function "showOfflineDataJQuery".

 

showOfflineDataJQuery: function()

{

          jQuery.sap.require("jquery.sap.storage"); 

oJQueryStorage = jQuery.sap.storage(jQuery.sap.storage.Type.local); 

          oJQueryStorage.put("myJQueryStorage", oModel.oData); 

 

var offlineData = oJQueryStorage.get("myJQueryStorage");

var items = [];

 

// looping over this offlineData to modify one of the property

// just for display purpose

 

for (var key in offlineData) {

if (offlineData.hasOwnProperty(key)) {

offlineData[key].DEPTDESC = "From JQuery Store";

               items.push(offlineData[key]);

 

}

}

 

var oJSONModel = new sap.ui.model.json.JSONModel();

               oJSONModel.setData({modelData: items});

               sap.ui.getCore().byId("emplOfflineTblJQuery").setModel(oJSONModel);

 

var oTemplate = new sap.m.ColumnListItem( 

{cells: [  

         new sap.m.Text({text : "{ECODE}"}), 

         new sap.m.Text({text : "{FIRSTNAME}"}),

         new sap.m.Text({text : "{LASTNAME}"}),

         new sap.m.Text({text : "{DEPTCODE}"}),

         new sap.m.Text({text : "{DEPTDESC}"}),

 

         ]

});

 

               sap.ui.getCore().byId("emplOfflineTblJQuery").bindItems("/modelData",oTemplate);

               app.to("id.JQuery"); // navigating to my second view

     },

 

     where,

          oModel is our OData Model

          myJQueryStorage is the name of our offline store

          offlineData contains the Employee records retrieved from offline store

          items is a JSON array consisting of Employee records retrieved from offline store

 


Test our app

 

Let's run our SAPUI5 app to test our offline store created using JQuery API's.

 

Click on "Show offline data JQuery" button;

Output7.png

 

You can view the table containing records retrieved from our local offline store created using JQuery API.

 

Output8.png

 

Also, you can inspect Resources to view the Offline Local Storage.


Output9.png


JQuery Storage v/s IndexedDB

 

Sr. No.

JQuery Storage

IndexedDB

01

Used for storing small amount of data

Used to store larger amounts of data

02

Simple to code & implement, supports synchronous API

Complex to implement but supports both synchronous & asynchronous API's

03

Provides simple storage like key-value pair

Provides a structured database like storage

04

Used for storing objects in the form of serialized strings

We can design Object Stores to have multiple columns/ fields

05

Provides methods like GET, PUT, REMOVE etc. for data operations

Use of Transactions to add, retrieve & remove data having success & error callbacks for exception handling

06

Does not provide special features like indexing/ key generator

Provides features like indexing and key generation

07

HTML5 storage - mostly supported by all browsers

May not be supported some browser (old versions)

08

Especially used for storing simple session specific values

Especially used for storing large number of records for building offline solutions

Viewing all 789 articles
Browse latest View live




Latest Images