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

UI5 Classes and Objects - Putting it all together

$
0
0

Dear UI5 Developers,

 

In my blogs about Classes and Objects, I've explained how you can use this in UI5:

UI5 Classes and objects

UI5 Classes and objects - Inheritance

 

But why and when should you use classes? I mostly use OData model to fetch the data from the backend system. If you bind the OData model to your view, it will automatically do an AJAX request to the backend.  But the two-way binding of the OData model is still experimetnal: OData Write Support - UI Development Toolkit for HTML5 (SAPUI5) - SAP Library . This means that we still can't use the model for input fields. This is why I choose to use JSON models and classes. With the JSON model I can use the two-way binding. The classes can be used to create objects and set default values from the backend. The objects can be changed by the input fields.

 

I've created an example where I store multiple values from input fields in a table. I've use an object Person which has one or more Skills. In my example you can add Skills to a Person.

 

We'll use a parent class to create a JSON model.

 

sap.ui.define([  "sap/ui/base/Object",  "sap/ui/model/json/JSONModel"
], function(Object,JSONModel) {  "use strict";  return Object.extend("be.wl.objects.model.BaseObject", {      constructor: function() {  this.model = new JSONModel();  this.model.setData(this);      },  getModel:function(){  return this.model;  }    });
});

 

The Person class will inherit from the Parent class. It will also have a function to add skills to the Person Object. After adding a skill, it will have to update the model with the refresh function:

 

sap.ui.define([    "be/wl/objects/model/BaseObject"
], function(BaseObject) {  "use strict";  return BaseObject.extend("be.wl.objects.model.Person", {      constructor: function(data) {   BaseObject.call(this);        if(data){          this.firstname = data.firstname;          this.lastname = data.lastname;          this.fullname = this.getFullName();        }        this.skills = [];      },      getFullName:function(){        return this.firstname+" "+this.lastname;      },      addSkill:function(skill){        this.skills.push(skill);        this.model.refresh();      }    });
});

 

The class Skill will just contain a name of the skill and a value. It will also inherit from the parent class.

 

sap.ui.define([    "be/wl/objects/model/BaseObject"
], function(BaseObject) {  "use strict";  return BaseObject.extend("be.wl.objects.model.Skill", {      constructor: function(data) {        BaseObject.call(this);        if(data){          this.name = data.name;          this.value = data.value;        }      }    });
});

I can now use this model in my view. Therefore I've to bind this in my controller. I bind my Person object "this.p" to the view as "person" model and an empty object of the Skill class to the "skill" model. I also store this empty skill object in my controller as "this.s".

 

      onInit:function(){        this.p = new Person({firstname:"Wouter",lastname:"Lemaire"});        this.getView().setModel(this.p.getModel(),"person");        this.s = new Skill({name:"name",value:"value"});        this.getView().setModel(this.s.getModel(),"skill");      },

Besides the init function, I have created a addSkill function. This is used to add a Skill to the Person object and set a new init object as "skill" model.

 

      addSkill: function() {        this.p.addSkill(this.s);        this.s = new Skill({name:"name",value:"value"});        this.getView().setModel(this.s.getModel(),"skill");      }

Without setting the "person" model again, it will update the tabel! We only have to set this once!

 

The result will look like this:

 

2015-12-23_14-03-18.png

 

You can find all the code on plunker:

 

http://plnkr.co/edit/mBEk9uBfJxRvRUxGAc2I?p=preview

 

Kind regards,

Wouter


Secure UI5 Apps with Neptune? Absolutely!

$
0
0

You want to introduce secure SAP UI5 Apps with Neptune in your company? But you or your colleagues have reservations about security?

How can we control access and be sure that only priviliged users can open and use the applications?

The Neptune Application Designer Version 3.0 contains tools for creating secure SAP UI5 applications!

Neptune offers a role based authorization concept. Using Neptune Application Management (NAM) you can create policies. They control the access to the apps. You can bind your existing SAP roles to these policies.

Depending on the policy the user can only see elements that where assigned to their SAP role.

NAD Policy – Role assignment

policyBlog1.jpg

You can create as many policies as you wish and assign them to elements like

  • Apps,
  • Menus &
  • Bundle-Apps

This way you can implement an authority check. (More information about the elements released in Neption 3.0 can be found here)

Note, that by using policies you can only restrict the user from seeing the apps on the launchpad. This offers only a weak protection because the apps can be called by a specific url outside of the bundle app.

In this case you have to implement a check using the Neptune Standard SAP Authorization. The app itself will check which user has called it and which SAP roles he has in the backend. If he has the needed roles, he can access the app. The deciding value here is the S_ICF object and the ICF_VALUE.

The ICF_VALUE that you have defined must be entered in the settings of your app in the Neptune Application Designer into the fieldSAP Authorization.

SAP AUTH using ICF_VALUE in NAD

SApauthBlog.jpg

Now your app is protected against unauthorised calls from the outside. (See: Neptune Support – Authorization)

If you have any problems concerning the security of your app, feel free to contact me using the comment section or email.

 

UI5 measure api design concerns

$
0
0

When I go throw jquery.sap.global.js, there is PerfMeasurement class implementation. I am afraid there are two concerns in my mind.

 

Firstly there are two different patterns to implement a class, one is prototype pattern, such as Version:

 

 

function Version() {}
Version.prototype.InRange() {}

The another pattern is constructor. Such as PerfMeasurement.

 

 

function PerfMeasurement() {     this._start = function() {};     this.start = this['_start'];
}


According to _start function name, it should be a private function. But we could access this private function.

 

 

> jQuery.sap.measure._start> function (sId, sInfo, aCategories) {                if (!bActive) {                    return;                }                aCategories = checkCategories(aCategories);                if (!aCategories) {                    return;                }                var iTime = jQuery.sap.now(…

 

Since private and public functions are defined in the same function scope, then private function could get function instance reference this, and public functions could access private functions. For example:

 

 

var JsClass = function(name) {    var that = this;    this.name = name;    function _sayHello() {        console.log('hello:', that.name);    }    this.say = function() {        _sayHello();    };
}
var instanceOfJsClass = new JsClass('Mike');
console.log('name:', instanceOfJsClass.name);
instanceOfJsClass.say();              // Access public function
instanceofJsClass._sayHello();     // Can't access private function

 

In the same way, _start could be removed bind from function instance reference this. And then _start function become a real private function, and the same time it could be accessed by other public functions.

UI5 Classes and objects - Singleton pattern

$
0
0

Dear UI5 Developers,

 

In UI5 you can create classes and use objects as explained in these blogs:

UI5 Classes and objects

UI5 Classes and objects - Inheritance

UI5 Classes and Objects - Putting it all together

 

In this blog I'm going to show you how the Singleton pattern can be used in UI5. This example is based on the example of the latest blog: UI5 Classes and Objects - Putting it all together

 

Singleton can be used as a shared controller to store global values like for example your JSON models. I often use it to implement AJAX calls to the backend.

 

To implement a singleton in UI5 you have to create a basic class. Instead of returning this class, you have to return a function that creates an instance of your Singleton class. This function will only make a new object/instance of the class the first time (if no instance/object exists) . To know if an instance/object is already created, you can use a global variable to keep the active object/instance.

 

In our example the Singleton will be used to create an object of the Person class. It will also have a function to add skills to a person. Last but not least it will also have a function to create an initial skill.

 

It will look like this:

sap.ui.define([  "sap/ui/base/Object",    "be/wl/objects/model/Person",    "be/wl/objects/model/Skill"
], function(Object,Person,Skill) {  "use strict";  var instance;  var services= Object.extend("be.wl.objects.model.services",{  constructor:function(){   this.person = {};  this.skill = {};  },  setPerson:function(firstname,lastname){  this.person = new Person({firstname:firstname,lastname:lastname});  },  newSkill:function(skill){  this.person.addSkill(skill);  },  getInitialSkill:function(){   this.skill = new Skill({name:"name",value:"value"});   return this.skill;  }  });  return {        getInstance: function () {            if (!instance) {                instance = new services();            }            return instance;        }    };
});

Now you can use the singleton in your full UI5 project with the following syntax:

 

services.getInstance()

I've update the viewcontroller to use the singleton class, it will look like this:

onInit:function(){
services.getInstance().setPerson("Wouter","Lemaire");
this.getView().setModel(services.getInstance().person.getModel(),"person");
this.resetSkill();
},
addSkill: function() {
services.getInstance().newSkill(services.getInstance().skill);
this.resetSkill();
},
resetSkill:function(){
this.skill = services.getInstance().getInitialSkill();
this.getView().setModel(this.skill.getModel(),"skill");
}

The function setPerson will create an object of the Person class. We can access all properties of the singleton instance:

services.getInstance().person

 

The function newSkill will add a skill to the peron object and the function getInitialSkill will create an empty Skill object.

 

The result will look the same as in the blog UI5 Classes and Objects - Putting it all together but now it's using a singleton class.

 

2015-12-23_15-21-09.png

 

You can find the full code example on plunker:

 

http://plnkr.co/edit/7y8FBnyRZLZDvnd2oIFS?p=preview

 

Kind regards,

Wouter

SAP UI5 Apps and the SAP Portal

$
0
0

Scenario

1. You are using the SAP Enterprise Portal (or SAP NetWeaver Portal)
2. You have configured Fiori Apps or your have developed your own UI5 apps with Neptune, Eclipse or WebIDE
3. You want to combine these two worlds

 

The Fiori Launchpad

Since version 7.4 the SAP NetWeaver Portal offers the Fiori Launchpad. The Fiori Launchpad is a framework page, that orientates itself closer to the W3C guidelines than the standard framework page. Additionally if offers the possibility to integrate the Fiori design into the portal environment. The following video introduces the Fiori Launchpad:

To use the framework page you have to perform a first configuration. You can find a relevant article here.

Integration of SAP UI5 Apps into the portal

The advantage of integrating your apps into the SAP Portal is the possibility to use your existing infrastructure and processes. The concepts of roles and iViews stay the same. To integrate a UI5 app into the Portal you just have to create an iView for the app and integrate it into a portal role.
Attention: You can run into errors if you use the "standard framework page" of the portal. Please use the "unified framework page" instead!

 

Excursus: UI5 Apps as a portal component

This blog describes building your own portal component that contains an UI5 application. Here exists the possibility to outsource your code into Java servlets and to build dynamic pages with JSPs. Starting here - analog to WebDynpro Java -RFC-calls can be made to connected backend systems. If your already have WebDynpro Java developers in your compand and strive for a change of strategy this is a possibility to use your existing ressources while preparing yourself for the future.
If you have any more questions or want to dig deeper, feel free to contact me.

From the Appbuilder to River RDE to WebIDE

$
0
0

The question in todays world is not: Do we switch to UI5? The question is: How do we develop UI5?
There are three different ways to do it:

This blog will focus on the SAP WebIDE to give you a quick overview.

 

First a quick info about the name

SAP introduced the WYSIWYG editor to develop web applications in 2013. It was first called "SAP App Builder". The product was enhanced in 2014 which caused a new name: "SAP River RDE". RDE stands for Rapid Development Environment. But this name didn't last long. The product is called "SAP WebIDE" now.

Features of the SAP Web IDE

As mentioned before the product has already changed a lot in its short lifespan. But it matured a lot. The original "App Builder" could only be used for building GUI prototypes. Today this has changed. The IDE offers:

  • Syntax Highlighting
  • Code Completion
  • Development of apps based on templates
  • Expanding SAP Fiori apps
  • Import & Export functionality
  • Mock-ups

To get a first impression you should watch the following video:

 

 

The downside?

The WebIDE is - as suggested by the name - a web application. This means an internet connection is needed to use it. But the bigger question is: Where is the source code stored? A lot of companies (outside of the US where cloud services are extremely popular) want to store their code on premise.  BUT: SAP offers a standalone installation of the WebIDE. A combination is also possible. You can use the WebIDE in the cloud (and therefore always use the recent version) and store the code on premise in a repository. The downside to the WebIDE therefore doesn't really exist.

The future of IDEs

IDEs in the browser seem to be the future. Google does it, too. Lots of advantages arise: the IDE is always updated and the building process will be executed on a central build server. This speeds up the building process. Re-developing of Eclipse plugins after a new release will be something from the past. The change of the IDEs will be a very interesting topic in the next years.

Summary

The WebIDE has developed into a very interesting and powerful alternative to Eclipse for UI5 development. I strongly suggest that you take a closer look at what seems to be the goto IDE for SAP UI5 development for the upcoming years.

Here you can learn more about SAP WebIDE. I hope that I could give you a good overview over the recent developments in the UI5 world and if there are any open questions feel free to contact me in the comment section or via mail.

An insight into Model, View, Controller (MVC) in the context of SAP UI5

$
0
0

Introduction : Multiple UI technologies follow the MVC design pattern including SAP WebDynpro Java, JSF etc and now SAP UI5. The blog describes the MVC design pattern. The content is compiled from multiple SAP sources but put in a single reorganised view to enhance understanding and highlight key information.

Target Audience : Consultants who wish to have a high level understanding to MVC towards SAP UI5 development.

Problem : multiple types of views (JS, JSON, XML, HTML) and new ways to expose services (OData, JSON, XML, Resource Models) make it a tough task to understand MVC in the perspective of SAPUI5. The SAP documentation is spread out across multiple sources.

Solution : This blog attempts to compile data from multiple sources and put it in a simple, structured way so that information read persists in memory.

Outcome : At the end of the blog you will have a fair understanding of MVC in the context of SAP UI5.

Date Created : 2 Jan 2016

Note : This blog is a living document, I will keep on enhancing it with more, better structured and information and formatted code with every iteration. So next time you come back to the blog it will better than before.

Capture_MVC.PNG

 

Model

A model in the Model View Controller concept holds the data and provides methods to retrieve the data from the database and to set and update data. A model object is a container for the data upon which your application operates. The business data within a model can be defined using various formats:

  • JavaScript Object Notation (JSON)
  • Extensible Markup Language (XML)
  • Resource Model
  • OData

 

Server Side ModelsClient Side Models
  • OData Model
  • JSON model
  • XML model
  • Resource Model
  • Intended for big datasets.
  • JSON and XML Models are intended for small datasets.
  • The Resource model is designed to handle data in resource bundles, mainly to provide texts in different languages (small in size).
The dataset is only available on the server and the client only knows the currently visible rows and fields. Only loads the data requested by the user interface from the server.Model data is loaded completely and is available on the client.
This also means that sorting and filtering on the client is not possible. Any changes in data binding or list operations require a new request to the server. Operations such as sorting and filtering are executed on the client without further server requests
  • The OData model supports two-way (default), one-way and one-time binding modes. However, two-way binding is currently only supportes for properties, and not for aggregations.
  • The JSON model supports two-way (default), one-way and one-time binding modes.
  • The XML model supports two-way (default), one-way and one-time binding modes.
  • The Resource model only supports one-time binding mode because it deals with static texts only.
JSON Model is much compact in comparison with XML model.

 

You can not only define one model for your applications, but define different areas in your application with different models and assign single controls to a model. You can also define nested models, for example, a JSON model defined for the application and an OData model for a table control contained in the application.

A web application should support several data sources, such as JSON, XML, Atom, or OData. However, the way in which data binding is defined and implemented within the UI controls should be independent of the respective data source. It is also possible to create a custom model implementation for data sources that are not yet covered by the framework or are domain-specific.

 

View

The view in the Model View Controller concept is responsible for defining and rendering the UI. SAPUI5 supports predefined view types.

Capture.PNG
Although XML and JSON notation has been introduced for SAPUI5 UI controls, the Model View Controller concept is also supported to facilitate traditional programmatic UI constructions.

The following predefined view types are available:

Types of ViewsExamples
  • XML view. (Note: The XMLView type supports a mix of XML and plain HTML.)

<mvc:View controllerName="sap.hcm.Address" xmlns="sap.ui.commons" xmlns:mvc="sap.ui.core.mvc">

  <Panel>

    <Image src="http://www.sap.com/global/ui/images/global/sap-logo.png"/>

    <Button text="Press Me!"/>

  </Panel>

</mvc:View>

  • JSON view.

{

  "Type":"sap.ui.core.mvc.JSONView",

  "controllerName":"sap.hcm.Address",

  "content": [{

  "Type":"sap.ui.commons.Image",

  "id":"MyImage",

  "src":"http://www.sap.com/global/ui/images/global/sap-logo.png"

  },

  {

  "Type":"sap.ui.commons.Button",

  "id":"MyButton",

  "text":"Press Me"

  }]

}

  • JS view.

sap.ui.jsview("sap.hcm.Address", { // this View file is called Address.view.js

 

getControllerName: function() {

  return "sap.hcm.Address"; // the Controller lives in Address.controller.js

},

 

createContent: function(oController) {

var oButton = new sap.ui.commons.Button({text:"Hello JS View"});

oButton.attachPress(oController.handleButtonClicked);

  return oButton;

}

 

});

  • HTML view.

<template data-controller-name="example.mvc.test">

  Hello

  <h1>Title</h1>

  <div>Embedded HTML</div>

  <div class="test test2 test3" data-sap-ui-type="sap.ui.commons.Panel" id="myPanel">

  <div class="test test2 test3" data-sap-ui-type="sap.ui.commons.Button" id="Button1" data-text="Hello World" data-press="doIt"></div>

  <div data-sap-ui-type="sap.ui.commons.Button" id="Button2" data-text="Hello"></div>

  <div data-sap-ui-type="sap.ui.core.mvc.HTMLView" id="MyHTMLView" data-view-name="example.mvc.test2"></div>

  <div data-sap-ui-type="sap.ui.core.mvc.JSView" id="MyJSView" data-view-name="example.mvc.test2"></div>

  <div data-sap-ui-type="sap.ui.core.mvc.JSONView" id="MyJSONView" data-view-name="example.mvc.test2"></div>

  <div data-sap-ui-type="sap.ui.core.mvc.XMLView" id="MyXMLView" data-view-name="example.mvc.test2"></div>

  </div>

</template>


View types are offered for selection as view options in the application creation wizard. If you use SAPUI5application development tools, you can also plug in other view types. For defining other or custom view types, you extend the base class

sap.ui.core.mvc.View.

 

Controller

SAPUI5 uses the controller to separate the view logic from the model logic. The methods used for controlling the data flow are implemented in the controller.

 

You define a simple controller without functions as follows:

  sap.ui.controller("sap.hcm.Address", {

   // controller logic goes here

});

 

The string in quotes specifies the controller name. The controller file is then named Address.controller.js.

Note: The suffix .controller.js is mandatory for controllers.

 

Lifecycle Hooks

SAPUI5 provides predefined lifecycle hooks for implementation. You can add event handlers or other functions to the controller and the controller can fire events, for which other controllers or entities can register.

 

SAPUI5 provides the following lifecycle hooks:

Lifecycle HooksDescription
onInit()Called when a view is instantiated and its controls (if available) have already been created; used to modify the view before it is displayed to bind event handlers and do other one-time initialization
onExit()Called when the view is destroyed; used to free resources and finalize activities
onAfterRendering()Called when the view has been rendered and, therefore, its HTML is part of the document; used to do post-rendering manipulations of the HTML. SAPUI5 controls get this hook after being rendered.
onBeforeRendering()Invoked before the controller view is re-rendered and not before the first rendering; use onInit() for invoking the hook before the first rendering

 

 

Please NoteExample
For controllers without a view, no lifecycle hooks are called.

sap.ui.controller("sap.hcm.Address", {

   onInit: function() {

      this.counter = 0;

   }

});

Event Handlers and Other Functions

In addition to lifecycle hooks, a controller can define additional methods that serve as event handlers or additional functionality offered by the controller.

sap.ui.controller("sap.hcm.Address", {

  increaseCounter: function() {

          this.counter++;

   }

});

 

Controller Conventions
Controller names are capitalized

Controllers carry the same name as the related view (if there is a 1:1 relationship).

Event handlers are prefixed with "on"

e.g. onInit, onExit, onBeforeRendering, onAfterRendering

Controller names always end with *.controller.js

 

Miscellaneous

http://help.sap.com/saphelp_hanaplatform/helpdata/en/91/f233476f4d1014b6dd926db0e91070/content.htm?frameset=/en/91/f285256f4d1014b6dd926db0e91070/frameset.htm&current_toc=/en/d0/1cd0b7be7f441cb6c56ad4577b428c/plain.htm&node_id=190&show_children=false

An insight into consuming WebServices from SAP UI5.

$
0
0

Introduction : Many times consultants can be in a situation where the preferred UI Technology is SAPUI5 but services are exposed as WebServices rather than an OData model. The below blog will help you understand how you can consume a WebService from SAPUI5 via an XML model and show it in SAPUI5 user interface. Although some code samples are scattered here an there, one has to spend quite some time to make a scenario work. By the end of this blog you will have a fair understanding of the scenario and code to make it work.

Target Audience : Beginners to Experts alike.

Environment : Eclipse (Mars) with SAPUI5 plugins, Google Chrome browser.

Date Created: 4 Jan 16


I used the following publicly exposed WebServices to create a scenario :

http://www.w3schools.com/xml/tempconvert.asmx

http://www.w3schools.com/xml/tempconvert.asmx?WSDL

 

A convenient online WebService client :

http://wsdlbrowser.com/

You need to have the WSDL and then you can easily generate XML request and response. This is one of many options but it is interesting that with this site you can work without a Web Service client installation.

 

When dealing with XML in string format you might have issues using double quotes " as XML also has them and diving your XML into multiple line. The problem can be resolved by using single quotes to represent strings and using string concatenation :

http://www.w3schools.com/js/js_strings.asp

Capture.PNG

Key code that does the job :

http://scn.sap.com/community/developer-center/front-end/blog/2014/07/23/consuming-web-service-in-sapui5

Sample Code (Thanks to Navya Krishna):

var request = "<Your input xml to the webservice>”; // more info in the appendix section

var response = "";

$.ajax({

     url : "Your webservice wsdl url",

     type : "POST",

     data : request,

     dataType : "text",

     contentType : "text/xml; charset=\"utf-8\"",

 

     success : function(data, textStatus, jqXHR) {

          response = data;

          console.log("SUCCESS");

     },

 

     error: function(xhr, status)

     {

          console.log("ERROR");

     },

 

     complete: function(xhr,status) {

         console.log("COMPLETE"); 

     }

});

NOTE:

1). Since the WebService is not on the same machine as the SAPUI5 application it causes a "No 'Access-Control-Allow-Origin' header is present on the requested resource." error

Capture.PNG

It can be resolved by using the inbuilt proxy. For e.g. url : "proxy/http/www.w3schools.com/xml/tempconvert.asmx?WSDL",  in the above code.


2). For using the mentioned WebService I had to get rid of  </SOAP-ENV:Envelope> and </SOAP-ENV:Body> tags in my response to make the scenario work.

response = response.replace('<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">', '');

response = response.replace("<soap:Body>", "");                   

response = response.replace("</soap:Body>", "");                  

response = response.replace("</soap:Envelope>", "");


3). Do not forget to add the libraries required for running the scenario in the Bootstrap

data-sap-ui-libs="sap.m, sap.ui.commons,sap.ui.table"

 

Sample code that can consumes a WebService Response, converts it into an XML model and displays the data in a Table :

NOTE: The code here does not run in a sequence if written right after Ajax call. Please use the success or complete functions to ensure code runs in a sequence.

var oModel = new sap.ui.model.xml.XMLModel(); 

function uicontrols(){

     oModel.setXML(response);

}

var oTable = new sap.ui.table.Table({

     id: "table1"

});

 

oTable.addColumn(

     new sap.ui.table.Column({

          label: new sap.ui.commons.Label({text: "Label1"}),       

          template: new sap.ui.commons.TextField().bindProperty("value", "Tag1/text()") 

     })

);

oTable.addColumn(

     new sap.ui.table.Column({

          label: new sap.ui.commons.Label({text: "Label2"}),

          template: new sap.ui.commons.TextField().bindProperty("value", "Tag2/text()")

  })

);

oTable.setModel(oModel);

oTable.bindRows({path: "/the main XML tag under which Tag1 and Tag2 are present/"});

 

Appendix :

Sample Request XML based on above mentioned public WebService

var request = '<?xml version="1.0" encoding="UTF-8"?>' +

  '<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://www.w3schools.com/xml/">' +

  '<SOAP-ENV:Body>' +

  '<ns1:CelsiusToFahrenheit>' +

  '<ns1:Celsius>10</ns1:Celsius>' +

  '</ns1:CelsiusToFahrenheit>' +

  '</SOAP-ENV:Body>' +

  '</SOAP-ENV:Envelope>';


Another sample code that simulates consuming a WebService Response, converting it into an XML model and displaying the data in a Table.

(Thanks to Konstantin Anikeev)

https://scn.sap.com/thread/3307300

// start of code

var oModel = new sap.ui.model.xml.XMLModel();

  oModel.setXML("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"+

  "<config>"+

  "<item date=\"January 2009\">"+

  "<mode>1</mode>"+

  "<unit>900</unit>"+

  "<current>1</current>"+

  "<interactive>1</interactive>"+

  "</item>"+

  "<item date=\"February 2009\">"+

  "<mode>2</mode>"+

  "<unit>400</unit>"+

  "<current>2</current>"+

  "<interactive>5</interactive>"+

  "</item>"+

  "<item date=\"December 2009\">"+

  "<mode>9</mode>"+

  "<unit>5</unit>"+

  "<current>100</current>"+

  "<interactive>3</interactive>"+

  "</item>"+

  "</config>");

 

  try{

  alert("GETXML>>>>:"+oModel.getXML());

  }catch(e){

  alert(e.message);

  }

 

  oTable.addColumn(

  new sap.ui.table.Column({

  label: new sap.ui.commons.Label({text: "Date"}),

  template: new sap.ui.commons.TextField().bindProperty("value", "@date")  }

  )

  );

  oTable.addColumn(

  new sap.ui.table.Column({

  label: new sap.ui.commons.Label({text: "Mode"}),

  template: new sap.ui.commons.TextField().bindProperty("value", "mode/text()")  }

  )

  );

  oTable.addColumn(

  new sap.ui.table.Column({

  label: new sap.ui.commons.Label({text: "Unit"}),

  template: new sap.ui.commons.TextField().bindProperty("value", "unit/text()")  }

  )

  );

  oTable.addColumn(

  new sap.ui.table.Column({

  label: new sap.ui.commons.Label({text: "Current"}),

  template: new sap.ui.commons.TextField().bindProperty("value", "current/text()")  }

  )

  );

  oTable.addColumn(

  new sap.ui.table.Column({

  label: new sap.ui.commons.Label({text: "Interactive"}),

  template: new sap.ui.commons.TextField().bindProperty("value", "interactive/text()")  }

  )

  );

  oTable.setModel(oModel);

  oTable.bindRows({path: "/item/"});

// End of code


Introduction to Neptune UX Platform 4.0

$
0
0

Neptune UX Platform 4.0 with new Launchpad & Neptune Fiori® Apps.

We will conduct a live demo of the new Neptune User Experience Platform (UXP) 4.0 and its new Launchpad, ready for release in mid-February 2016 with already available Neptune Fiori® Apps.

New look and feel and added functionality such as: 500px_by_281px

  • New navigation
  • Split view running multiple apps
  • Smart apps with usage history
  • Notification features
  • Fast and user friendly customization
  • New PDF viewer
  • And more

This webinar will be held by the head of the regional sales offices and the technical team represented by a developer. The focus will be on Neptune UXP 4.0, its new Launchpad and its features. Through demonstration of Neptune Fiori® Apps in our new launchpad you will see how seamlessly the apps work together, the rapid response time and how fast you will be able to perform your processes.  Business modules covered will include SD-MM-HCM-WM-PM.

The webinars will be held on Wednesday January 20th at 1:00 pm GMT (Europe) and 11:00 am PST (America). You can sign up for the appropriate webinar link on the registration buttons at the bottom of this page. The webinar will open 15 minutes before it starts and we encourage you to join early as there is a limit of 500 attendees.

Join this webinar to experience Neptune UX Platform 4.0 and learn how you can implement it, WITHOUT the need for expensive and time consuming upgrades:

2015-11-10 17.19.59

 

  • Fiori® UX applications with Offline and native device capabilities
  • App Store with ready to go apps imported into your system in just 2 clicks
  • No need for additional software or hardware upgrades
  • Easy to use drag and drop designer targeting traditional ABAP developers for customization
  • Re-use of your current SAP landscape, custom code, roles and authorizations
  • Low TCO, minimal risk, and fast ROI

 

 

Neptune Software is the most cost efficient, fast and secure way to design, build, manage and run your Fiori® apps.
By directly leveraging ABAP in our drag and drop designer, we maximize the use of the customer’s existing SAP set-up, while minimizing the need for integration.
One solution covers the complete app lifecycle.

Join us in this webinar for a introduction to Neptune UXP 4.0 and its new Launchpad & Neptune Fiori® Apps.

Wednesday, January 20, 2016 1:00 pm- 2:00 pm  GMT (Europe)

Skjermbilde 2015-04-23 kl. 09.38.33

 

 

 

Wednesday, January 20, 2016 11:00 am – 12:00 pm PST (America)

Skjermbilde 2015-04-23 kl. 09.38.33

Dynamic image manipulation with UI5 and Promises

$
0
0

 

Intro

You know that time when manipulating height and width of an image just doesn't get the job done? This is when a lot of additional players enter the game quickly - HTML5's canvas, Promises and runtime Image objects.

We're talking asynchronous content handling here by retaining control over runtime execution along with operating a drawing canvas - all of that in UI5, via JavaScript.

Good thing that we're going to cover all that ground here

Although every aspect of client-side image manipulation at runtime would deserve its own blog post (even more so when looked at from a UI5-perspective), let's go for the sweep through.

 

Why asynchronous?

Sure thing: working over an image with JavaScript could be done synchronous à la (attention, pseudo-code)

var oImage = Page.getImage();
oImage.resizeTo(1500,1000);
Page.put(oImage);

This would result in the image getting resized - with the user potentially closing the browser window in the process.

Why? Because synchronous JS-operations lock the UI.

If they run for a noticeable amount of time (we're talking single-digit secons here), it appears to the user that the browser has frozen. This causes frustration, the user might cancel the operation by killing the browser - bad UX.

 

So the rule of thumb is: when doing extensive JS operations client-side, do them asynchronous in order not to lock up the UI.

 

Why Promises?

Sure thing: you could use callback functions to go asychronous with your code (attention, pseduo-code again)

var oImage = new Image();
var oImageNew = new Image();
oImage.src = Page.getImage();
oImage.onload = function() {  oImageNew = oImage.resizeTo(1500,1000);  Page.put(oImageNew);
}

Here, the onload event of an image is utilized by an anonymous callback function to resize the image asynchronously (after the original image is ready for processing).

Fine, right? Well, almost.

Because now you certainly want to do other stuff after the resizing has finished.

Which means, you'd have to stuff everything in the anonymous callback function (attention, pseudo-code, you know)

oImage.onload = function() {  oImageNew = oImage.resizeTo(1500,1000);  Page.put(oImageNew);  Page.showPopup("Resizing finished!");  oDownloadButton.show();  // anything else
}

 

Imagine a couple of asynchronous callback-based operations in a process.

Sooner or later, you end up in callback hell (by now, you should know, pseudo-code and such)

getImage(function(a){    getAnotherImage(a, function(b){        createDownloadLink(b, function(c){            showDownloadButton(c, function(d){                ...            });        });    });
});

 

Wouldn't it be nice to be able to:

getImage()  .then( resizeIt )  .then( createDownloadLink )  .then( showDownloadButton )

?

Answer: yes, it would be. Awesome even!

 

And that's what Promises in JS are for: they let you retain control over sequence in asynchronous operations.

(Technical excursion: compared to callbacks, they give you return and throw back, along with a meaningful runtime stack for debugging. Elaborate on that? Not here. See beginning of blog post, "every aspect ... would deserve its own blog post" bla bla).

 

For the record: Why canvas?

Because literally there is no other way to dynamically manipulate images client-side at runtime.

 

So, after this lengthy intro:

let's get started

 

(Upfront: go grab or look at the source over at GitHub. Way easier to have that open simultaneously in order to get the bigger picture.)

 

Here's an image in UI5:

<Image                    id="moray_eel"                    src="moray_eel.jpg"                    press="transformPic" />

 

We want to do some show-casing manipulation on it (transformPic) after it is clicked.

First step is to get the base64-string representing the image.

That's where we already dive into the Promise-world (no more pseudo-code from here on, promised (ha ha)):

 

The Promise

 

sap.ui.define([                "sap/ui/core/mvc/Controller"            ], function (Controller) {                return Controller.extend("vb.controller.main", {                    sBase64: '',                  transformPic: function (oEvent) {                                              this.toBase64(oEvent.getSource().getProperty("src"))                                // store resulting base64 as property                                .then(function (sBase64) {                                    this.sBase64 = sBase64;                                }.bind(this)) // handle context!                        // ...                  }          })
});

Quick excursion: notice the bind(this) above: in the async world, one key to success is to always watch your context. Meaning, to what scope the this keyword applies to when the code is executed. One way of doing this is to bind the current scope via (duh) bind(this) to a function that is executed at a later point in time, in a different scope.

 

Back to toBase64(): it is defined as a Promise. Stripped to bare minimum, it looks like:

 

toBase64: function (sBinaryImgSrc) {                        return new Promise(                                    // do some stuff                                    // ...                                    if (everythingOk === true) {                                    // resolve a.k.a "return" the Promise                                        resolve(someValue);                                    } else {                                   // sth went wrong                                    // -> reject the Promise a.k.a "report" the error                                        reject(someError);                                    }                                }                        );                    }

Promises give you exactly 2 options: resolve or reject them.

When resolving, return a value. Or just return. Important thing is, that you resolve() at all.

When rejecting, think of it as of throw-ing an error.

 

This is where you regain control: the Promise "spawns" its own thread, executes the stuff you want it to, and then tells you afterwards whether it was successful (resolve) or not (reject). So you get to know, when your async operation has finished - now you can continue doing other stuff (asynchronously) or handle the error:

 

 

this.toBase64(oImgSrc)    .then( function(resultOfToBase64) {        // do sth with resultOfToBase64        return doWhatEver(resultOfToBase64);    })    .then( function(resultofWhatEver) {        return doMore(resultofWhatEver); // doesn't have to be a Promise, can be any function returning stuff    })    .then( doEvenMore ) // shorthand notation of calling the Promise doEvenMore with the result of doMore    .catch(function (oError) {        console.log(oError);    });

 

Following this principle, you can either continue then-ing by

- calling other Promise functions or

- just returning values/functions.

 

The entire promisey way of then-ing depends on returning values/functions or calling other Promises (which themselves just return stuff).

 

Read the above again. "The entire...". And again. "The ...".

Because this is where most errors happen: if your functions/events/Promises are not executed in order, but somehow different, it is most likely you forgot to return/resolve at one point.

(Technical excursion: there's a whole lot of variations on how to call functions, use more than one parameter for the subsequent then-d call (named functions!) etc pp. As mentioned at the beginning of the post: "every aspect ... would deserve its own blog post" bla bla).

 

Now that we know how to control the sequence of the asynchronous operations, here's how to manipulate images in UI5 at runtime.

 

The image manipulation

As mentioned above, using HTML5's canvas for the purpose is a given. Only so can an image be repainted on a canvas (ha ha).

Here's where we use the Promises: we load a base64-string onto a canvas, make modifications and then return the modified image, again as base64-string.

 

Let the source code and the comments speak for themselves:

 

return new Promise(                                function resolver(resolve, reject) {                                    // construct an "anonymous" image at run-time                                    var oImage = new Image();                                    // trigger onload                                    oImage.src = sBinaryImgSrc;                                    oImage.onload = function () {                                        // construct canvas                                        var oCanvas = document.createElement("canvas");                                        // make canvas fit the image                                        oCanvas.width = this.width;                                        oCanvas.height = this.height;                                        // reduce opacity of context, then applied to the image                                        var oContext = oCanvas.getContext("2d");                                        oContext.globalAlpha = 0.2;                                        oContext.drawImage(this, 0, 0); // paint the image onto the canvas                                        // retrieve the manipulated base64-represenation of the image from the canvas                                        var sBase64 = oCanvas.toDataURL("image/jpeg", 1.0); // retrieve as JPG in 100% quality                                        // "return" it                                        resolve(sBase64);                                    };                                    // sth went wrong                                    // -> reject the Promise a.k.a "report" the error                                    oImage.onerror = function (oError) {                                        reject(oError);                                    };                                }                        )

Following this approach, you can basically manipulate the image any way HTML5's canvas allows for.

See the example for reducing opacity, inverting color scheme and resizing images (resulting in new images).

 

Speaking of resizing:

1. the use case for this is simple - say, you allow for picture uploads by your users. For performance reasons, you don't want to use the original uploaded image everywhere in your UI5 application. Instead, you want a lower-quality version, e.g. for previews. That's when you need store additional versions of the uploaded image for later use. You can generate as many variations of the uploaded picture at runtime with UI5 and ...

2. ...the help of using Promises in batch-mode.

What?

Yes, batch-mode. Meaning: trigger multiple asynchronous operations simultaneously and get notified when the last one(!) has finished.

The asynchronous equivalent of the synchronous forEach-loop so to speak.

This is what Promise.all(aPromises) is for. It takes an array of Promises as argument, executes each one and will only resolve() after the last one has finished - returning you the result of every Promise of the argument-array in return. Isn't that awesome?!?

Look at line 124ff. of the example file for an (duh) example of using Promise.all() - for batch-resizing an image.

 

Conclusion

So here we are: having used UI5, canvas and Promises in order to manipulate images at runtime.

All of this in an asynchronous way, keeping the UI unlocked, allowing for simultaneous application behavior.

Good stuff!

But especially Promises in JS are hard to understand just by looking at them once. At least for me. It took me some time to get my head around the concept and the coding embedded in UI5. So I encourage you to get your hands dirty as well, fiddle with and/or look at the code and/or look at the screenshots attached.

Happy coding!

 

tl;dr

use JS Promises in UI5 to load base64-strings on canvas to generate new images.

Example source

What is the difference between sap.ui.require and jQuery.sap.require

$
0
0

There are two similar APIs in UI5, jQuery.sap.require and sap.ui.require. These two APIs is a little confused, especially for beginners from experienced with RequireJS.

jQuery.sap.require

The detailed API explanation in developer guide. Unfortunately You can not search in API REFERENCE.

This api will (synchronously) load the specified js file and do execution. The js file is not need to be a module. It could be a plain object, or just a piece of code.

synchronously is confirmed. Please refer to requireModule function in jQuery.sap.global.js

  1.   jQuery.ajax({
  2.   url : oModule.url,
  3.   dataType :'text',
  4.   async :false,
  5.   success :function(response, textStatus, xhr){
  6.   oModule.state = LOADED;
  7.   oModule.data = response;
  8.    },
  9.   error :function(xhr, textStatus, error){
  10.   oModule.state = FAILED;
  11.   oModule.errorMessage = xhr ? xhr.status +" - "+ xhr.statusText : textStatus;
  12.   oModule.errorStack = error && error.stack;
  13.    }
  14.    });

For example, if there is an UI5 app and MessageBox is not loaded. There will be a error, when you try to access it with sap.ui.commons.MessageBox .

  1. > sap.ui.commons.MessageBox
  2. UncaughtTypeError:Cannot read property 'MessageBox' of undefined(…)
  3. (anonymous function)@ VM265:2
  4. InjectedScript._evaluateOn @ VM41:878
  5. InjectedScript._evaluateAndWrap @ VM41:811
  6. InjectedScript.evaluate @ VM41:667

As MessageBox will be exported to global, so once it is be loaded, we could use it. So we usejQuery.sap.requier to load and execute the script.

  1. >var returnObject = jQuery.sap.require('sap.ui.commons.MessageBox');
  2. <.undefined
  3. > sap.ui.commons.MessageBox
  4. <.Object{Action:Object,Icon:Object}
  5. > returnObject
  6. <.Object{log: B, measure: G, _mIEStyleSheets:Object, interaction:Object, fesr:Object…}

There is an ajax call for MessageBox:

  1. Request URL:https://sapui5.hana.ondemand.com/resources/sap/ui/commons/MessageBox.js
  2. RequestMethod:GET
  3. StatusCode:200 OK

After the MessageBox is loaded via jQuery.sap.require, we could use it.

Note:
jQuery.sap.require will return a wired object. I guess this api applies for chain pattern, then it will return the context this. But for this scenario, I don't think it is a good idea. Since there are some property should be explored to API consumer and it will confuse. And the second parameter fnCallback is never be used.
  1.   jQuery.sap.require =function(vModuleName, fnCallback){
  2.    returnthis;
  3.    }

sap.ui.require

Quote documentation in UI5 API REFERENCE

  1. Synchronous Retrieval of a Single Module Value 2.Asynchronous Loading of Multiple Modules

For item 1, it is very similar with jQuery.sap.require. Except two:

  • url pattern: it use slash (/)
  • return object is module object

For example:

  1. >var oMessageBox = sap.ui.require('sap/ui/commons/MessageBox');
  2. <.undefined
  3. > oMessageBox
  4. <.Object{Action:Object,Icon:Object}

For item 2, it is used to load dependency module:

  1. > sap.ui.commons.MessageBox
  2. <.UncaughtTypeError:Cannot read property 'MessageBox' of undefined(…)
  3. >(function(){
  4.   'use strict';
  5.   sap.ui.require(['sap/ui/commons/MessageBox'],function(MessageBox){
  6.   console.log('MessageBox:',MessageBox)
  7.   })
  8. }());
  9. <.undefined
  10. <.MessageBox:Object{Action:Object,Icon:Object}

Actually load modules is not async, since it is also use requireModule to load file. But documentation is marked as async. : (

Usage of sap.ui.ux3 Library

$
0
0

Hi Every one ,

As i got a requirement to create Comment Section in my Project.I have searched in lot of websites but i cant find any direct example.I have tried with different libraries given by sap.In that i found sap.ui.ux3 is Plug and play library where we can directly use this library with minimal coding.

This library provides good functionality where in we can post the comment and for that again there is a reply section which is almost similar to our linked in or Facebook Comment section.

Deferred OData requests with SAPUI5 and jQuery

$
0
0

Introduction

 

This blog post describes how to call multiple OData requests in parallel and wait all them to finish so that when we have all the responses and after that do something else.



Use case

 

Imagine that you have two services, first for data and second for authorizations, and you have to activate a button only if first service gives you certain data and second service gives you correct auths. You have to do following two steps:

 

  1. Request data to a OData service 1
  2. Request data to OData service 2 to check authorizations

 

But only If 1 and 2 are done and data is retrieved then you have to activate a button.

 

For that you can use jQuery deferred objects and Promises.

 

Check following liks for help if you are not familiar with that concepts.

 

https://api.jquery.com/jquery.deferred/

https://api.jquery.com/jquery.when/

 

Some examples

https://learn.jquery.com/code-organization/deferreds/examples/

 

 

Let's start with SAPUI5

So we will create one Deferred object for each condition we want to wait and resolve the object when required.

 

In following example with jQuery.Deferred() we create the object. After that we can create an ODataModel and attach to the metadataLoaded event. Finally when metadata is loaded the Deferred object will be resolved.

 

            this.oModel2DataDeferred = jQuery.Deferred();                    var sURI2 = "https://cors-anywhere.herokuapp.com/services.odata.org/V3/OData/OData.svc/";            var oModel2 = new sap.ui.model.odata.v2.ODataModel(sURI2,oConfig);                    oModel2.attachEventOnce("metadataLoaded", function() {                console.log("oModel2: Metadata loaded OK");                this.oModel2DataDeferred.resolve();            }, this);                    var finishWait = function(){                console.log("this is the end");            }

 

We can increase the complexity. We will call another service and another Deferred object but now Deferred object will be resolved when we have a response for a requested data. So if we read the Employees and request was ok, success function will be read and Deferred object is resolved.

 

 

            this.oModelDataDeferred = jQuery.Deferred();                                      var sURI = "https://cors-anywhere.herokuapp.com/services.odata.org/Northwind/Northwind.svc/";            var oModel = new sap.ui.model.odata.v2.ODataModel(sURI,oConfig);              oModel.attachEventOnce("metadataLoaded", function() {                console.log("oModel: Metadata loaded OK");                      }, this);                              oModel.read("/Employees", {  success: jQuery.proxy(fSuccess, this),                error: jQuery.proxy(fError, this)  });            function fSuccess(oEvent){             console.log("oModel: Employees read successfully!");             this.oModelDataDeferred.resolve();            };            function fError(oEvent){             console.log("oModel: An error occured while reading Employees!");            };

 

How do we wait both Deferred objects to be resolved? Using jQuery.when that returns a Promise that will be resolved as soon as all Deferred objects passed as parameters are resolved.

 

In our example function finishWait will only be called if both Deferred ( oModelDataDeferred & oModel2DataDeferred ) are resolved, that also means that finishWait is called if:

 

  1. Employees for service ( http://services.odata.org/V4/Northwind/Northwind.svc/ ) are read
  2. Metadata for service ( http://services.odata.org/V3/OData/OData.svc/ ) is ready

 

              var finishWait = function(){                   console.log("this is the end");             }            jQuery.when(this.oModelDataDeferred,                        this.oModel2DataDeferred )            .done( jQuery.proxy(finishWait,this) );

 

jsbin example

 

If you check the example you will see the following messages in the console but the order of appearence of messages 1 2 & 3 is never known. Message 4 will be always the last message for sure.

 

  1. "oModel2: Metadata loaded OK"
  2. "oModel: Metadata loaded OK"
  3. "oModel: Employees read successfully!"
  4. "this is the end"

 

 

Conclusion

With that we have parallel batch requests with SAPUI5 and we have a way to wait all requests using jQuery.

I hope this blog helps someone with same requirements and if you have any doubts or recomendations do not hesitate to contact me

 

Thanks a lot!

Lesson and Learnt from my first Fiori UI5 APP

$
0
0

Eventually my first custom-developed Fiori UI5 APP went live before Christmas Holiday last year. I used to attend some UI5 and WEB UI projects but this was the first time that I designed UI screens, developed OData services and UI5 application.  Basically this APP is to provide search function for field technicians and supervisors to view historical orders and navigate to the order APP to check the detail.

screen.jpg

 

Although it was not easy to deploy the first one, I really enjoyed the development journey. The more UI5 Development was done, the more I learnt from it and felt happy. Now it is time for me to summarize lesson and learn for future UI5 development.

Project Approach: Agile rather than ASAP

As SAP developers, we all are familiar with ASAP project methodology. But in UI5 and UX development we tend to apply Agile approach to deliver the project. It means that we will have more user requirement and experience discussion sessions during the project. Every discussion session we demo the function and collect user feedback and requirement. Then modify the APP and function and prepare for the next demo session. Eventually the developers and business user reach common agreement with the APP.

Development tool: Eclipse or WEB IDE

UI5 development beginners always ask which UI5 development tool is better, Eclipse or WEB IDE. In my opinion, WEB IDE is much better than eclipse. WEB IDE provides SAP UI5 development templates which allow you to quickly build an application project with basic function.  You do not need to spend a lot time to consider syntax and project structure. As WEB IDE set up testing connection with Backend gateway system, you are able to test your UI5 development function directly with WEB IDE .But with Eclipse you need to deploy the development to the gateway system first then conduct testing, which waste some times.

More ABAP Less UI5

As SAP UI5 solution is a combination of UI5 and ABAP OData, you can achieve part of certain function in UI5 and part of it in ABAP backend. I would like to suggest to try to move more function to be achieved in ABAP rather than in UI5. For example, you can control the display of certain field through UI5 directly or through an indicator from ABAP OData. I would prefer to choose control through ABAP OData because it reduce the logic in WEB UI side which improve performance in UI.

Clean Cache

Cleaning cache to show the updated development is a continuous work which I need to do in the development phase. Otherwise when you fail to achieve certain function, you will always doubt if you clean cache already or not. Below 3 transaction codes I think are the most important to clean cache. If you could  not clean cache with below TCodes, I believe  it is not cache issue. You should consider other ways to solve the problem.

/iwfnd/cache_cleanup

/iwbep/cache_cleanup

SMICM invalidate cache globally

 

Debugging

In case of issues, I used Google Chrome Developer Tools to debug and set break point. Console can show you all the errors inside the WEB APP. You also can disable cache and check the OData request and response through Network tab of developer tool.

 

So far, I think compared with Native IOS or Andriod APP, UI5 APP are not as good as them. The major drawback is not support offline mode and some APP control still need to be improved. But I believe Fiori UI5 is the future of SAP. We do need to skill up to obtain the mobility development skill as a SAP Developer.

Happy Coding.

Using Geo Maps with SAP UI5

$
0
0

Lately I had to visualize geographical data in a SAPUI5 app. As I had already built a set of SAPUI5 charting controls (see here ) I decided to incorporate a Leaflet Maps control into my collection. In this post I will describe how you can use Leaflet maps in your own SAPUI5 application.


2016-01-07 13_43_58-Bundestagswahl 2009.png

 

Before we begin we will need a geojson file that contains the shapes of the geographical data we would like to plot. Basically such a file is a normal json file and looks like this:


{
"type": "FeatureCollection",                                                                          
"features": [
{ "type": "Feature",
"id": 0,
"properties": { "Name": "Mitte",  },
"geometry": { "type": "Polygon", "coordinates": [ [ [ 13.40352847102303, 52.54021229004892, 0.0 ]
]]}}]}

In this example I will be using a geojson file of Berlin´s districts.

 

Lets start by creating a simple UI5 App and by adding the required dependencies for D3 and the AkuaJs charting library. The geojson file will be included like any other script file in a <script> tag so we can make sure it is loaded before we start drawing the map. Additionally we will include a Leaflet CSS file.


<script src="https://rawgit.com/Qrist0ph/AkuaJs/latest/dist/plain/libs/D3/d3.min.js"></script><script data-main="https://rawgit.com/Qrist0ph/AkuaJs/latest/dist/plain/main.js" src="https://rawgit.com/Qrist0ph/AkuaJs/latest/dist/plain/libs/require.js"></script><link rel="stylesheet" href="https://rawgit.com/Qrist0ph/AkuaJs/latest/dist/plain/libs/nvd3/nv.d3.min.css" type="text/css" /><link rel="stylesheet" href="https://rawgit.com/Qrist0ph/AkuaJs/latest/dist/plain/libs/leaflet/leaflet.css"><script src="berliner-bezirke.geojson"></script>

 

Next we will just define some quantitative data, add the map control to the app and hook it up to our data. The AkuaJs library is based on a multidimensional  data model so every data point has the form E(D("Bezirk"), "Mitte") where the D() function defines the dimension and the E() function defines an element of the corresponding dimension. A value is assigned by creating a Tuple with the T() function: T([E(D("Bezirk"), "Mitte")], 88) . The assignment of a quantitative value to  a corresponding shape in the geojson file is based on the value passed in the E() function and the "Name" property in geosjon file. A more detailed documentation of the AkuaJs API can be found here.

The Map control is added like shown in the listing below and has the following properties that need / can be configured:

 

  • axis0: here we pass the list of districts that corresponds to the shape names in the geojson file
  • connection: here we pass the quantitive data that needs to be visualized
  • color: the color of the shape
  • numberFormats: the number format in D3 notation
  • mapCenter: the Longitude / Latitude coordinates of the map
  • zoom: the initial zoom factor
  • geoJson: the variable containing the geoJson object
  • click: a function to listen to click events

The geojson file used in this example can be found here.


<!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="https://rawgit.com/Qrist0ph/AkuaJs/latest/dist/plain/libs/D3/d3.min.js"></script>    <script data-main="https://rawgit.com/Qrist0ph/AkuaJs/latest/dist/plain/main.js" src="https://rawgit.com/Qrist0ph/AkuaJs/latest/dist/plain/libs/require.js"></script>    <link rel="stylesheet" href="https://rawgit.com/Qrist0ph/AkuaJs/latest/dist/plain/libs/nvd3/nv.d3.min.css" type="text/css" />    <link rel="stylesheet" href="https://rawgit.com/Qrist0ph/AkuaJs/latest/dist/plain/libs/leaflet/leaflet.css">    <script src="berliner-bezirke.geojson"></script>    <script src="https://openui5.hana.ondemand.com/resources/sap-ui-core.js"            id="sap-ui-bootstrap"            data-sap-ui-libs="sap.m"            data-sap-ui-theme="sap_bluecrystal">    </script>    <script>        jQuery.sap.registerModulePath('AkuaJs', 'https://rawgit.com/Qrist0ph/AkuaJs-UI5/latest/build/AkuaJs');        jQuery.sap.require("AkuaJs.Map");        eBezirkMitte = E(D("Bezirk"), "Mitte");        eBezirkPankow = E(D("Bezirk"), "Pankow");        eBezirkFriedrichshain = E(D("Bezirk"), "Friedrichshain");        new AkuaJs.Map({            axis0: A({                crosslists: [TCL([T([eBezirkMitte]), T([eBezirkPankow])])]            }),            connection: [                 T([eBezirkMitte], 88),                 T([eBezirkPankow], 33),                 T([eBezirkFriedrichshain], 55)],            color: "#FFD7AA",            numberFormat: ",.2",            mapCenter: [52.50, 13.2],            zoom: 10,            geoJson: geojson,            click: function (t, v) { alert(t); }        })         .placeAt('content')        ;    </script></head><body class="sapUiBody" role="application">    <div id="content"></div></body></html>



OData model extension to consume plain REST services

$
0
0

Hello to all,

 

While searching arround for ways to consume a plain REST server with OpenUI5, I noticed there were only a few possibilities.

The JSON model was one of them.

But also the extension found here :http://scn.sap.com/community/developer-center/front-end/blog/2015/09/29/extending-a-control-in-the-sapui5-library-sapuimodeljsonjsonmodel

 

However, they did not fit my needs.

So, I decided to write my own. With some success.

 

My new try can be found on Github.

https://github.com/LongDirtyAnimAlf/OpenUI5_RestModel

 

It includes an example to consume the Github REST api for its users and repos.

The result looks like this:

SAPGithubDemo.JPG

 

 

Main things to consider:

 

Normal REST does not prove metadata !!

 

Some of it is self-generated by this REST model.

Do do so, it needs info that is provided by setting a new parameter: the key.

(See source in Detail.controller.js)

 

oRepoTable.bindItems({  path : sRepoPath,  sorter : {   path : 'name',   descending: false  },  parameters :{    key:'id'  },  template:this._oTemplate
});

The new REST model self generates the metadata.

Many methods are are adopted to be able to create metadata.

 

RestModel.prototype._createmetakey = function(sPath, mParameters) {
var that = this;
if (!that.oMetadata.mEntityTypes[sPath]) {
that.oMetadata.mEntityTypes[sPath] = {};
}
var oEntityType = that.oMetadata.mEntityTypes[sPath];
if (!oEntityType["key"]) {
oEntityType["key"] = {};
}
var aKey = oEntityType["key"];
if (!aKey.name) {
if (mParameters && mParameters.key) {
aKey.name=mParameters.key;
} else {
if (that.sKey) {
aKey.name = this.sKey;
} else {
aKey.name = "ID";
}
}
}
jQuery.sap.assert(aKey.name, "Severe key error !!!");
};
// intercept creation of bindings to create metadata
RestModel.prototype.createBindingContext = function(sPath, oContext, mParameters, fnCallBack, bReload) {
// get key from params or settings
// mimics metadata info
this._createmetakey(sPath, mParameters);
return ODataModel.prototype.createBindingContext.apply(this, arguments);
};

Data retrieval is done by a normal ajax request.

This made it necessary to override some of the data retrieval methods.

 

Github uses different keys for different services.

users: login

repos: id.

To accomodate for this, keys are set in the main view and in the detail controller.

 

A main key can be set for the model as a whole.

 

var oModel = new sap.ui.model.rest.RestModel("https://api.github.com");
oModel.setKey("login");

 

Please remember: this is work in progress. Readonly access for now.
Feedback is very welcome.

SAPUI5 walkthrough step 1: Hello World! Where where it all began

$
0
0

> Welcome to my SAPUI5 Walkthrough, And Dive In blog series, where I’ll not only walkthrough the tutorial, but dive in, to explore the magic behind SAPUI5. Buckle up and sit tight, it's gonna be a bumpy ride as I’m no expert in any way shape or form, just a humble learner who loves coding and likes to get to the bottom of things =。=


Happy new year everyone !


Let’s travel back in time, back to step 1, which we have not yet talked about, instead of creating a Hello World web page, I got something else prepared for you, let’s dive in to talk about how does the BaseObject, the base class for all SAPUI5 objects get created, where it all began. (btw, it feels strange to me every time seeing the word "class" in the JavaScript context...the base class - BaseObject, what? ←_←)


In the process of doing that, I’ll give my best shot to try to explain things like how does “class” look like in the JavaScript / SAPUI5 context, what’s the implementation behind SAPUI5 inheritance. For the JavaScript / SAPUI5 gurus out there, please please let me know if there’s anything explained incorrectly, thank you.


I had created a repository on github for all of my SAPUI5 source code study findings.


Okay, before we dive into the framework source code, allow me to talk a bit about OO in JavaScript in general to the best of my knowledge, to prepare our journey. Bare with me, let me try to do that in one single screenshot.


OO in JavaScript

---

screenshot#1

 

step.1.1.png

Allow me to add one more drawing to hit these concepts home. (Thanks to Mr.Kyle Simpson's You Don't Know JS book series which helped me greatly to understand these)


JavaScript function, object, prototype, constructor, __proto__ relationships illustrated

---

screenshot#2

 

step.1.2.png

I think we’re ready to dive into the framework, see how the BaseObject get created, let’s start by setting a break point here:

screenshot#3

step.1.3.png

 

Before we step into the craeteClass method, let’s talk about how does SAPUI5 create a class from a high-level.


The code above (between line 34,963 and 34972) is essentially this if we strip it down to the bare minimum:


 

var BaseObject = function() {    // complain if 'this' is not an instance of a subclass    if ( !(this instanceof BaseObject) ) {        throw Error("Cannot instantiate object: \"new\" is missing!");    }
}


This does look familiar, right? Same as the Person “class” example we had at the beginning of our post, if that’s all we need to create a “class” in JavaScript, what else does SAPUI5 createClass offers? Here’s a couple (not all) of them:


What does createClass method offer?

---


- our class can be made visible as a global JavaScript object (sap.ui.base.Object).

- with the classInfo object, you can define the constructor function separately, which is something you can’t do with vanilla JavaScript, `Klass.prototype.constructor  = function() { // constructor implementation }` won’t work.

- with the classInfo object, you can define metadata to your class as well.

- with the classInfo object, besides constructor and metadata, additional functions can be added into the prototype object.

- our class can pick up a convenient “static” method called extend, which makes our class extensible (inheritable).


Alright, enough with the trailers, let’s step into the Metadata.createClass method and get the show started ! (screenshots below reflect the key points during framework code execution, with inline explanations / narrows)


screenshot#4

step.1.4.png

 

screenshot#5

step.1.5.png

 

screenshot#6

step.1.6.png

 

screenshot#7

step.1.7.png

 

screenshot#8

step.1.8.png

 

screenshot#9

step.1.9.png

 

screenshot#10

step.1.10.png

Create the metadata.

 


There’s too much screenshots already, I won’t get into the create metadata in detail, but one thing interesting to look at during the metadata creation process, see how the prototype object get enriched with the classInfo object.


How does prototype object get enriched?

---

screenshot#11

step.1.11.png

 

screenshot#12

 

step.1.12.png

Just like that, a new class, called BaseObject get created, as you can see, next is that, methods (key value pairs, method name as the key, and function as the value) will be added on the BaseObject prototype object.

screenshot#13

step.1.13.png

 


The End.

UI5 offline application using ServiceWorker API

$
0
0

Topic

This project demonstrates how to develop an offline capable OpenUI5 application using the new ServiceWorker API which needs no native code.

 

 

Project Sources: https://github.com/pensoffsky/OpenUI5-ServiceWorker

You can try it out in current Chrome, Opera or Android browsers: https://pensoffsky.github.io/OpenUI5-ServiceWorker/index.html

(Open the link in your browser and let the app load. This puts all the needed files into the cache of the ServiceWorker. Then close your browser, go into airplane mode and reopen the URL. The app should start normally. If it does not work then maybe your browser is not supported yet. List of supported browsers)

 

 

Disclaimer

  • this concept does unfortunately not work with "normal" UI5 resources because they use sync XMLHttpRequests which are not supported by the ServiceWorker API. The project uses modified UI5 resources created by matz3 that load async.
  • only https is supported by ServiceWorker because of security reasons
  • the ServiceWorker API is not yet supported in all browsers. You can check the availability here
  • the delayed loading of the UI5 resources was adapted from this concept: Asynchronous load of SAPUI5

 

Features

  • Graceful degradation
    • if the ServiceWorker API is not available in the browser than the feature is ignored and it works as a normal online application
  • Start of app
    • the first time you open the application all needed resources are loaded from the server and cached in the browser. After this the next time you open the application it is served directly from the ServiceWorker cache. This means you can disconnect your internet connection and still open the app in the browser just by navigating to the URL.
  • Querying of stock price
    • if you have an internet connection then the http request goes directly to the yahoo webservice. The result is then written to the ServiceWorker cache and displayed in the app. (the timestamp in the response changes)
    • if you are offline then the request is answered from the cache. The last successful cached request is returned.

 

Start of app ONLINE

Here you see that the request to yql (yahoo api) is live and 2.5KB in size. So the user gets live data when he clicks the "refresh stock price" button.

OpenUI5-ServiceWorker-Online.png

Start of app OFFLINE

The browser is set to Offline mode and the page is reloaded.

Here you see that the request to yql (yahoo api) is answered from the ServiceWorker cache after the initial request failed. So the user gets the stock price from the last time he used the application.

OpenUI5-ServiceWorker-Offline.png

Files

  • index.html
    • main entry file. Installs the ServiceWorker, loads OpenUI5 resources and instantiates the app.
  • sw.js
    • contains the ServiceWorker definitions
    • the new Fetch API is used extensively
  • dist/
    • location of the OpenUI5 resources

 

index.html

Depending on the availability of the ServiceWorker API  the ServiceWorker is registered. Only after the registration is over then the UI5 resources are loaded.

if ('serviceWorker' in navigator) {        navigator.serviceWorker.register('sw.js').then(function(registration) {            // Registration was successful            console.log('ServiceWorker registration successful with scope: ', registration.scope);            //now load the UI5 resources            fLoadUI5();        }).catch(function(err) {            // registration failed            console.log('ServiceWorker registration failed: ', err);            //now load the UI5 resources            fLoadUI5();        });    } else {        //if the browser does not support serviceworker then just load the app        fLoadUI5();    }

 

sw.js

First we define which files we want to cache for offline use

var urlsToCache = [    '/',    '/dist/resources/sap-ui-custom.js',    '/dist/resources/sap/m/library-preload.json',    '/dist/resources/sap/ui/core/themes/sap_hcb/library.css',    '/dist/resources/sap/m/themes/sap_hcb/library.css',    '/dist/resources/sap/ui/core/themes/base/fonts/SAP-icons.ttf',    '/dist/resources/sap/ui/thirdparty/unorm.js', //needed for safari    '/dist/resources/sap/ui/thirdparty/unormdata.js' //needed for safari
];

then we hook the install event of the ServiceWorker. This is used to add all the defined URLs to the cache.

self.addEventListener('install', function(event) {    // Perform install steps    event.waitUntil(        caches.open(CACHE_NAME)        .then(function(cache) {            console.log('Opened cache');            return cache.addAll(urlsToCache);        })    );
});

 

we also hook the fetch event. This event is triggered when the webApp is doing a http request. We check based on the URL what the app is requesting and what caching logic we want to use.

self.addEventListener('fetch', function(event) {    if(event.request.url.startsWith("https://query.yahooapis.com")){            ...        } else {        ...    }
});

For the yahoo API we first try to get the data via http request and only if this does not work we answer the request from the ServiceWorker cache. When the http request was successful we add it to the cache so that is is available for the next offline start of the application.

console.log('ServiceWorker: yahoo query: detected');        //for yahoo queries: online first             var fetchRequest = event.request.clone();        event.respondWith(fetch(fetchRequest).then(          function(response) {            // Check if we received a valid response            if(response && response.status === 200 ) {                console.log('ServiceWorker: yahoo query: fetch OK');                var responseToCache = response.clone();                //valid webrequest, clone and cache the result                caches.open(CACHE_NAME).then(function(cache) {                    console.log('ServiceWorker: yahoo query: added to cache');                    cache.put(event.request, responseToCache);                });                return response;            } else {                console.log('ServiceWorker: yahoo query: fetch FAILED');                //webrequest FAILED, try to answer from cache                caches.match(event.request).then(function(response) {                    //we dont care if the response was ok or NOT                    console.log('ServiceWorker: yahoo query: answered from cache' + response);                    return response;                });;            }          }      ).catch(function(error){          console.log('ServiceWorker: yahoo query: fetch EXCEPTION' + error.message);          //webrequest FAILED, try to answer from cache          return caches.match(event.request).then(function(response) {              //we dont care if the response was ok or NOT              console.log('ServiceWorker: yahoo query: answered from cache' + response);              return response;          });;      }));

The static resources we always try to get from the ServiceWorker cache and only if this does not work we do a live http request.

event.respondWith(            //check if the request can be answered from the offline cache            caches.match(event.request).then(function(response) {                if (response) {                    // Cache hit - return response from cache                    console.log('ServiceWorker: found:' + event.request.url);                    return response;                }                console.log('ServiceWorker: NOT FOUND:' + event.request.url);                return fetch(event.request);            })        );

Use Fiddle to make modifications on framework js file

$
0
0

Sometimes for trouble shooting or research purpose, you would like to make small changes on framework js file, and test how your application would react to those changes.

 

For example I would like to add a new line for debugging purpose before line 70.

clipboard1.png

It is not possible to make any modifications on the formatted js file done by Chrome "Pretty print" button.

clipboard2.png

We can only change original unformatted file or switch the source code to debugger version and change Text-dbg.js instead. Unfortunately changes done by both approaches could not be persisted. Once we refresh Chrome, they are gone.

clipboard3.png

Inspired by Alessandro Spadoni's great blog Switch #openui5 version on-the-fly without changing the code - Web Debugging Proxy, now I can use Fiddle to achieve the requirement once and for all:

 

1. Download the original Text.js to local laptop. Add the code you would like to insert and save the change.

clipboard4.png

2. Open Fiddle, run UI5 application and use Fiddle to capture the network traffic. Find the corresponding session to request Text.js, and drag it to tab "AutoResponder" and drop there. Select the two checkbox "Enable rules" and "Unmatched requests passthrough".

clipboard5.png

Once you finished drop, the Text.js url will be automatically populated to Rule Editor.

clipboard6.png

Select "Find a file" from drop down list and specify the local modified file you have done in previous step.

clipboard7.png

Once done, you have now created one rule as below: every time the Text.js with given url is accessed, Fiddle will serve it with your local modified version as response.

clipboard8.png

Now we can re-launch application and have a test. We can observe that this time, the modified Text.js is returned:

clipboard9.png

And in Chrome development tool, we can also see modified source code of Text.js.

clipboard10.png

Anchor Tab in SAP UI5

$
0
0

Hello Folks,

I would like to contribute the Anchor tab working Model.As we have this example in Xml View,but it is bit tricky to replicate same to js view.

I am attaching the working project of the Anchor Tab.Please let me know your suggestions about this application

Viewing all 789 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>