Quantcast
Channel: SAPUI5 Developer Center

Input: How to highlight suggestion items

$
0
0

Hello All,

 

Input control is where the user can enter data. It has some good built-in features like setting the type, suggestion items pop-up, value help dialog etc.

 

I remember there was a question on SCN few months back about how to highlight input suggestion items. I had provided a solution for it, but that seems to fail during string comparison and also finding list pop-up id dynamically.


So, I have to re-built the code to overcome above issues.

My JSON Model would be:

          var aData = [{            "ProductId": "1239102",            "Name": "Power Projector 4713",            "Category": "Projector"          }, {            "ProductId": "2212-121-828",            "Name": "Power Tag",            "Category": "Graphics Card"          }, {            "ProductId": "K47322.1",            "Name": "Webcam Smaill",            "Category": "Graphics Card"          }, {            "ProductId": "22134T",            "Name": "Webcam",            "Category": "Accessory"          }, {            "ProductId": "P1239823",            "Name": "Monitor Locking Cable",            "Category": "Accessory"          }, {            "ProductId": "214-121-828",            "Name": "Laptop Case",            "Category": "Accessory"          }, {            "ProductId": "215-121-828",            "Name": "Laptop Small",            "Category": "Accessory"          }, {            "ProductId": "XKP-312548",            "Name": "USB Stick 16 GByte",            "Category": "Accessory"          }, {            "ProductId": "KTZ-12012.V2",            "Name": "Deskjet Super Highspeed",            "Category": "Printer"          }, {            "ProductId": "KTZ-12012.V2",            "Name": "Deskjet Super1",            "Category": "Printer"          }, {            "ProductId": "KTZ-12012.V2",            "Name": "Deskjet Super2",            "Category": "Printer"          }];

And the XML view code:

<mvc:View height="100%"    controllerName="demo.InputPage"    xmlns:l="sap.ui.layout"    xmlns:model="sap.ui.model"    xmlns:core="sap.ui.core"    xmlns:mvc="sap.ui.core.mvc"    xmlns="sap.m">      <Input id="oInput"          showSuggestion="true"          showValueHelp="false"          suggest="onSuggest"          suggestionItems="{                            path: '/'                            }">        <core:Item text="{Name}" key="{ProductId}" />      </Input></mvc:View>

Now according to my JSON Model, when user types in the name, input suggestion would pop-up highlighting the characters that are typed.

To do that, I hear the suggest event of Input, where I can fetch the value that is entered and compare it with value that is shown on pop-up.

 

Here is the code, which does highlighting:

onSuggest: function(oEvent) {    var oValue = oEvent.getParameter("suggestValue");    var that = this;    jQuery.sap.delayedCall(100, null, function() {        that.highlight(oValue);    });
},
highlight: function(text) {    var oInput = this.getView().byId("oInput");    var oList = oInput._oList;  //Get Input List popup    var inputText = oList.$().find('.sapMSLITitleOnly');  //Find the title that needs to be highlighted    var oItems = oList.getItems();    $.each(inputText, function(i, value) {       var innerHTML = inputText[i].textContent;   //Get the title content       var oTitle = oItems[i].getTitle();    //Get Items title       var index = innerHTML.indexOf(oTitle);    //Compare and find the title index        if (index >= 0) {          innerHTML = innerHTML.substring(0, index) + "<span class='highlight'>" + innerHTML.substring(index, index + text.length) + "</span>" + innerHTML.substring(index + text.length);          inputText[i].innerHTML = innerHTML;    }
});
}

Interesting part was getting the index when we compare it with a string with case-sensitive.

Consider my first value in model, which is 'Power Projector 4713', when user types 'P' which is in caps, it works fine. If user types 'p' which is in small case then it doesn't find the index and returns '-1'.

 

To overcome above issue, I fetch list pop-up aggregation 'items' where I get the normal text using item getTitle() method and compare it with Input pop-up item title. Now it gives me the index where I can successfully highlight the pop-up text.

'Highlight' style class has been used for highlighting text:

    .highlight {      background-color: #ffff66;    }

Working demo of Highlight Input Suggestion Items: Plunker - Input Suggestion Items (Highlight)

 

Working Snip:

Capture.gif

 

Thanks for reading my blog, have a nice day!

 

Regards,

Sai Vellanki.


Custom Control: Highlightable Text

$
0
0

Inspired by Sai's blog Input: How to highlight suggestion items

 

It is common that we need to highlight text in table, list and other container controls. And we do not wish to iterate through all the items to highlight the text.

One way to handle this is to use a custom control and a model. The former is responsible for highlighting the text and the latter is responsible for informing the control what is the text to highlight.

 

Here is the JSBIN

JS Bin - Collaborative JavaScript Debugging

 

As an enhancement, we can alter this control to do underlining or/and handle regular expression for highlighting text.

 

Thanks

-D

SAPUI5 Table: Dynamic Creation based on CSV data

$
0
0

Hello All,

 

'CSV' is a simple format used to store tabular data, such as spreadsheet or database. Data in the CSV format can be imported.

 

Now, in our case we have the data placed in CSV format. Using sap.ui.unified.Uploader, we can upload 'CSV' document and has an ability to restrict the file type using 'setFileType' method.

 

Once the document is uploaded, we can convert it to JSON format by parsing through CSV data and store the JSON data into a model.

 

        handleUpload: function(oEvent) {          var that = this;          var oFile = oEvent.getParameter("files")[0];          if (oFile && window.FileReader) {            var reader = new FileReader();            reader.onload = function(evt) {              var strCSV = evt.target.result; //string in CSV               that.csvJSON(strCSV);            };            reader.readAsText(oFile);          }        },        csvJSON: function(csv) {          var lines = csv.split("\n");          var result = [];          var headers = lines[0].split(",");          for (var i = 1; i < lines.length; i++) {            var obj = {};            var currentline = lines[i].split(",");            for (var j = 0; j < headers.length; j++) {              obj[headers[j]] = currentline[j];            }            result.push(obj);          }          var oStringResult = JSON.stringify(result);          var oFinalResult = JSON.parse(oStringResult.replace(/\\r/g, ""));          //return result; //JavaScript object          sap.ui.getCore().getModel().setProperty("/", oFinalResult);          this.generateTable();        }

From above code Table data is ready. Now, we will have to fetch the column name labels. To fetch key names from model, we can use following method:

          var oModel = sap.ui.getCore().getModel();          var oModelData = oModel.getProperty("/");          var oColumns = Object.keys(oModelData[0]);

Store the key names into different property on same model and create the template that need to be used for 'columns' aggregation.

          var oColumnNames = [];          $.each(oColumns, function(i, value) {            oColumnNames.push({              Text: oColumns[i]            });          });          oModel.setProperty("/columnNames", oColumnNames);          var oTemplate = new Column({            header: new Label({              text: "{Text}"            })          });

For 'items' aggregation we can use the default binding path which has data and create the template that need to be used:

          var oItemTemplate = new ColumnListItem();          var oTableHeaders = oTable.getColumns();          $.each(oTableHeaders, function(j, value) {            var oHeaderName = oTableHeaders[j].getHeader().getText();            oItemTemplate.addCell(new Text({              text: "{" + oHeaderName + "}"            }));          });

Now getting it to all together, bind 'columns' / 'items' aggregation with respective binding paths and templates.

Finally, table gets generated based on CSV data. In result, no need to re-code if you want to use different CSV documents. You can just upload and read it.

 

Full working demo of Dynamic Table creation based on CSV data:Plunker

Working Snip:

Capture.gif

 

Thanks for reading my blog, have a great day.


Regards,

Sai Vellanki.

Cross Application Navigation between SAPUI5 applications

$
0
0

At SAP Inside Track Hamburg 2016 together with Remo Bettin we demonstrated during a short lightning talk how to do Cross Application Navigation between different applications on the Fiori Launchpad.

 

While this topic is covered in SAPUI5 help it is still not so easy to implement it. Therefore we built a small demo application in order to demonstrate how to use it.

 

Based on the entities of the Northwind OData Service we created four separate SAPUI5 Applications, one for orders, others for the corresponding customers and suppliers of the orders shown in the Orders app. The last application we used is based on the categories entity of the Northwind service.


All these applications are added to the Fiori Launchpad in the HANA Cloud Platform (which is named flpnwc there and available as a subscription in the HCP account).

Bildschirmfoto 2016-06-15 um 22.03.16.png

Picture 1: Fiori Launchpad in HCP showing the applications

 

In order to be able to use cross app navigation all applications need to be assigned to intents. The intent consists of a semantic object and an action on this object. For our applications we used the entity name as semantic object and all applications use the "display" action to display some data. The semantic object is declared in the manifest.json from the SAPUI5 app.

 

Bildschirmfoto 2016-06-20 um 15.26.11.png

Picture 2: Navigation properties in manifest.json

 

Once an application is reached it uses inner app navigation to find the correct route to a view.

The following example shows the routes in the supplier app. Here to navigate to a supplier detail we use a route "Suppliers/5" in order to open the object view and show details of supplier with ID 5.

 

Bildschirmfoto 2016-06-20 um 15.23.49.png

Picture 3: Routes in manifest.json

 

In order to navigate from one application to another in the Fiori Launchpad (FLP) we need to implement some code at the following locations:

 

1. The starting point of our CrossApp-Navigation is pressing on the supplier column in the Orders app. There we added the onPressSupplier-event in the column of the detail view where the supplier is shown

 

 

onPressSupplier: function(oEvent) {  var supplier = oEvent.getSource().getBindingContext().getProperty("Product/SupplierID"); // read SupplierID from OData path Product/SupplierID  var oCrossAppNavigator = sap.ushell.Container.getService("CrossApplicationNavigation"); // get a handle on the global XAppNav service  var hash = (oCrossAppNavigator && oCrossAppNavigator.hrefForExternal({  target: {  semanticObject: "Supplier",  action: "display"  },  params: {  "supplierID": supplier  }  })) || ""; // generate the Hash to display a Supplier  oCrossAppNavigator.toExternal({  target: {  shellHash: hash  }  }); // navigate to Supplier application  }

2. In the onInit-Event of the Master view of the Suppliers App we attach the event _onMasterMatched:


this.getRouter().getRoute("master").attachPatternMatched(this._onMasterMatched, this);

3. In the _onMasterMatched we read the Startup parameters where we get the Supplier ID:


_onMasterMatched :  function() {  var supplierID;  var startupParams = this.getOwnerComponent().getComponentData().startupParameters; // get Startup params from Owner Component  if ((startupParams.supplierID && startupParams.supplierID[0])) {  this.getRouter().navTo("object", {  supplierID: startupParams.supplierID[0]  // read Supplier ID. Every parameter is placed in an array therefore [0] holds the value  }, true);  } else {  this.getOwnerComponent().oListSelector.oWhenListLoadingIsDone.then(  function(mParams) {  if (mParams.list.getMode() === "None") {  return;  }  supplierID = mParams.firstListitem.getBindingContext().getProperty("SupplierID");  this.getRouter().navTo("object", {  supplierID: supplierID  }, true);  }.bind(this),  function(mParams) {  if (mParams.error) {  return;  }  this.getRouter().getTargets().display("detailNoObjectsAvailable");  }.bind(this)  );  }


Demonstration

Based on the entities of the Northwind OData Service we created four separate SAPUI5 Applications. In the Orders application you see a Master-Detail navigation for selecting an order and displaying the corresponding order details.


We select one order in the left master view and after that click on Customer "Vins et alcools Chevalier" with CustomerID "VINET":

Bildschirmfoto 2016-06-15 um 22.10.32.png

 

This creates the link "#Customer-display?customerID=VINET&/Customers/VINET".

The action "display" in the application with semantic object "Customer" is invoked.

 

The startup parameter is "customerID=VINET". This invokes the inner app navigation in the Customer app resolving in the URL fragment "/Customers/VINET" which is the route to the object view and shows the details view of customer "Vins et alcools Chevalier".

 

Bildschirmfoto 2016-06-15 um 22.11.26.png

 

Similar routing happens when clicking on the supplier in the orders app:

Bildschirmfoto 2016-06-15 um 22.12.02.png

 

Question

When using the sap.ushell.services.CrossApplicationNavigation.toExternal() method you can optionally provide a component (the root component of the application). Unfortunately in the help you can't find a hint for what this could be used. Maybe somebody knows the intent of this parameter?

 

Hopefully this blog post will be of some help for your implementation of Cross application navigation. In case you still have questions I would be happy to answer them if possible.

 

Cheers,

 

Mark

My Journey to UI5 - Part 1

$
0
0

A little preamble

 

I’m writing this document more for my help in keep track of my success and, even more, my errors, following Fiori’s master rule “fall first, fall often”.

I’m using a real case from my company to try UI5: it’s a bit scaring since if things will not sort out well, it will be a real problem!

This will not be a pure technical blog, but a more descriptive one, with links over links to the sources I used.

The tech parts will follow in later entries.

 

My background

When I read about UI5, I was scared. Literally.

It has been almost 12 years since I developed anything for the web (in early 2Ks I worked with PHP) and surely I do not remember the last time I worked with JavaScript!

So it has been mandatory and useful get the bases to avoid running around like a drunken chicken trying to understand what a single row of code does.

There are a bunch of sources where you can learn and refine your JS skill:

  1. https://developer.mozilla.org/en-US/Learn/JavaScript
  2. http://javascriptissexy.com/how-to-learn-javascript-properly/
  3. http://www.w3schools.com/js/
  4. https://www.codecademy.com/
  5. https://mva.microsoft.com/training-topics/web-development#!level=100&jobf=Developer&lang=1033

 

I focused on Codeacademy (I heard a lot of stories about it and I was curious to try it out) and JavascriptIsSexy (hot name!).

I know the choice is not brain-guided, but it ended up with me understanding the basics and the core concepts of JavaScript so I’m pretty satisfied.

I encourage everyone to try out as many tutorials and site as possible to find which methodology and style fit better for herself.

 

Learn UI5’s basics

I was aware about the difference between OpenUI5 and SAPUI5 and, even if in a far away future my company will probably require to switch to OpenUI5, I sticked as starting point with SAPUI5 due the larger (and more familiar to me) community on SCN.

So I downloaded Eclipse ADT with all the option except the one for SAP Hana (we are on 7.31) and then… well, where I start from?

 

The best way I found out to learn something about UI5 has been following the tutorial "Walkthrough" https://sapui5.netweaver.ondemand.com/#docs/guide/3da5f4be63264db99f2e5b04c5e853db.html

And with “following the tutorial” I mean, following it step by step, deleting everything and trying to redo it by myself, adding or removing the pieces just to try.

This procedure (“fall first, fall often”, do you remember?) reminded me when I was a child and play with Lego: the first couple of times I followed the instructions, then I always tried to build my galleon or my spaceship just “guessing” the right way.
I do not need to say how many times I failed and throw everything away (yes, I’m still talking about my first app), but I think that’s the best way to catch up with the mechanisms of something.

It has been hard, really hard due my steamy temper, discovering that my ABAP skill were useless in this world and that I keep crushing and crashing against silly things I never ever cared about (because with common SAPGUI and ABAP objects/FM we many nice features out of the box while with UI5 you have to think about them).

But they really were? No, as you'll find out in later blogs if you find interesting following them, exactly the opposite.

 

O Data, OData, wherefore art thou OData?

Even if you can build a standalone app without any connection with SAP, we all know that we HAVE to communicate with SAP, reading and writing data, so, when I somehow mastered the basic concepts of MVC and UI5, I moved to study how SAP can generate an OData service.

After a bit of googling, I found this guide (maybe not so recent since it’s from 2012) which is written really well by Bertram Ganz and Bernhard Siewert

http://sapassets.edgesuite.net/sapcom/docs/2012/11/44779e8c-5b7c-0010-82c7-eda71af511fa.pdf

If you, like me, are new to OData Services, follow this guide and in a couple of hours you’ll have your Service ready and running.

Another guide, a bit more recent, I used a lot is http://scn.sap.com/docs/DOC-61821 from  Mohamed Meeran

 

Both of the guides rely on FMs to manage the data retrieval and update and, even if there is nothing wrong with them, I looked for something more… nice: http://www.abap-developers.com/2014/07/simple-openui5-application-i-how-to-create-odata-model-using-segw/

http://scn.sap.com/people/peter.marcely’s guide showed me how to bypass the FM and to use directly the classes generated for the Services.

I found it better, since I got less objects to manage (a dictionary structure and the classes) and I stick with OOP which is always nice in my opinion.

 

I got the ropes now, and all the tools available for my first app: I got my Lego’s pieces, all scattered around, a full set of instructions and a monster headache because I do not know from where to start…

 


Understand extend function in Component.js in Fiori application

$
0
0

For every Fiori application we have Component.js, and there is a function extend() call. See one example below.

What is the purpose of this function call and what functionality would it achieve?

clipboard1.png

In order to understand the logic of extend, it is necessary to understand prototype concept in JavaScript.  Let's have a look at some more simple example.

 

See this simple html source code:

<html><script>
function Animal(name) {
this.sName = name;
console.log("constructor in Animal");
}
Animal.prototype.speak = function() {    console.log("My name is " + this.sName);
};
function Cat(name) {    Animal.call(this, name);    console.log("constructor in cat");
}
Cat.prototype = new Animal();
var animal = new Animal("Jerry");
animal.speak();
var cat = new Cat('Tom');
cat.speak();
console.log("cat is animal?" + ( cat instanceof Animal ));
console.log("cat is Cat?" + ( cat instanceof Cat ));
console.log(cat.constructor);</script></html>

I use prototype inheritance to achieve the class inheritance logic which is commonly used in other language like Java.

cat instanceof Animal and cat instanceof Cat both return true as expected.

 

There are two flaws of this inheritance solution:

1. every variable created by function constructor has one attribute __proto__, which points to the prototype object of its constructor. This could be verified by the fact that statement "cat.__proto__ === Cat.prototype " returns true.

In this example, you can find there are actually duplicate attribute sName, one in cat itself and the other one in its __prototype__.

clipboard2.png

2. in above code line 17, I try to build the inheritance relationship from Animal to Cat. Here a new instance of Animal is created. Suppose in productive code if we have a complex implementation of Animal, this initialization could consume unnecessary resource and memory to finish.

 

So another approach is used:

 

<html><script>
Function.prototype.extend = function(parent) {
function dummy() {};
dummy.prototype = parent.prototype;
this.prototype = new dummy();
this.prototype.constructor = this;
}
function Animal(name) {
this.sName = name;
console.log("constructor in Animal");
}
Animal.prototype.speak = function() {    console.log("My name is " + this.sName);
};
function Cat(name) {
Animal.call(this, name);
};
Cat.extend(Animal);
var cat = new Cat("Jerry");
cat.speak();
console.log("cat is Cat?" + (cat instanceof Cat));
console.log("cat is Animal?" + (cat instanceof Animal));
console.log(cat.constructor);</script></html>

Both flaws in first approach are avoided:

 

1. No duplicate attribute "sName" in prototype chain now.

2. The initialization of a new instance of Animal when trying to build prototype chain is avoided. Instead here I use a very light weighted, dummy function as a bridge to connect Animal and Cat. The trick is done among lines 5 ~ 8 below. Once done, every variable created by constructor Cat has its __proto__ points to Animal.

clipboard3.png

Now we have already enough knowledge to understand the extend() call done in Componnet.js in Fiori application.

 

1. extend call will call Metadata.createClass.

clipboard4.png

2. In Metadata.createClass, the essential idea of line 333 is just exactly the same as the code in my second prototype inheritance approach:

 

function dummy() {};

 

dummy.prototype = parent.prototype;

this.prototype = new dummy();

 

clipboard5.png

a. within jQuery.sap.newObject, a dummy object will be created:

clipboard6.png

b. here the argument oPrototype is the prototype of sap.ca.scfld.md.ComponentBase:

clipboard7.png

Once prototype chain is built, the function call jQuery.sap.setObject is called to expose cus.crm.opportunity.Component as a global JavaScript object:

clipboard8.png

Finally these below are what we have got:

 

1. we can directly access cus.crm.opportunity.Component in JavaScript code or console thanks to jQuery.sap.setObject call.

clipboard9.png

2. The prototype chain from sap.ca.scfld.md.ComponentBase to cus.crm.opportunity.Component is built, which could be confirmed by the picture below:

clipboard10.png

Now when I enter the context of my application, I can get the instance of this component via this(controller reference).getOwnerComponent().

 

From the belowing two test statement in console, I can ensure that the prototype chain is built:

 

1. ownComponent.__proto__.__proto__.constructor === sap.ca.scfld.md.ComponentBase returns true

2. ownComponent.hasOwnProperty("getMetadata") returns false and ownComponent.__proto__.__proto__.hasOwnProperty("getMetadata") returns true.

clipboard11.png

clipboard12.png


from returned metadata, we can find all the metadata information defined in the application and passed from extend call:

clipboard13.png

UI5 debug flag and Local Storage - how is your setting persisted?

$
0
0


We know that once we enable debug mode via "Ctrl+Alt+Shift+P", this setting will be persisted: even if you turn off your laptop and launch the application tomorrow, the debug mode will still be there.

clipboard1.png

In Chrome development tool, there is an Application tab which records this setting via key value pair using Local Storage.

clipboard2.png

As usual we can still use debug to investigate what has happened when the checkbox "Use Debug Sources(reload)" is clicked.

 

In Chrome development tool Elements tab, click the small arrow icon to enter the element inspection mode, then click the checkbox, then its html source code will be automatically navigate.

clipboard3.png


Perform global search via keyword "useDbgSources", and then we can find the event handler onUseDbgSources for debug mode set:

clipboard4.png

Set a breakpoint on this function, then mark the checkbox in UI, breakpoint is triggered as expected:

clipboard5.png

Within the function, we can know the debugging flag is set via localStoage API: window.localStorage.setItem

clipboard6.png

This API will change the following file in my laptop:

clipboard7.png

clipboard8.png

If I open the changed file via some SQLite management tool, I can find the corresponding entry with key sap-ui-debug and value 'X' set:

clipboard9.png

Compare Data Binding mechanism: SAPUI5 and Angular

$
0
0


Recently I am studying Angular in my spare time and I have written one post Compare Event handling mechanism: SAPUI5 and Angular.

And now l would like to share with you what I have learned from Angular about its data binding design.

 

I first list a comparison from my point of view and will illustrate it in detail.

UI5

Angular

1. should explicitly create or obtain Model

object to finish data binding

1. Could leverage HTML native elment as data model

2. Binding mode: one way, two way, one time

2. two way

3. Use object instance like

JSONPropertyBinding to hold binding path

information

3. Use Watcher object instance to hold binding

path information

4. grammar in xml view: {|model name|>/|field path|},

for example {json>/ImgSrc}

4. grammar in html page: see example below

For UI5 data binding, I have already written two self study blogs: Part6 control data binding and Part7 Implementations for different binding mode: OneWay, TwoWay, OneTime.


I use the following simple Angular application which only contains 11 lines to demonstrate the data binding implementation of Angular.

<html ng-app>  <head>    <meta charset="utf-8">    <title>Angular.js Example</title>    <script src="angular/angular.js"></script>  </head>  <body>    Name:<input ng-model="name" type="text"/>    Hello {{name}}  </body></html>

Here I have a input element marked with attribute 'ng-model="name"', which means I tell Angular that I have declared a data model with name "name". Then for "Hello {{name}}", it is actually a template and I tell Angular to render the page with static text "Hello" plus the actual content of input field.


You can test the application here. Every time you type something in the input field, the characters you have typed will be displayed after "Hello":

clipboard1.png

In this blog, I will explain how these 11 lines of codes have implemented the data binding feature, with the help of Angular framework code.


When you launch the application for the first time, the application will look like below. Although you only saw blank field in input field and initial value after "Hello", some logic has already been executed under the hood. I will first introduce how the initial value is rendered in html page.


How initial value is rendered in html page

 

1. Like UI5, Angular has also its bootstrap phase. During this phase, Angular will traverse the HTML DOM tree. Once it detects the input element marked with attribute "ng-model", it will create a watch object for current scope:

clipboard1.png

The watcher object is just an object consisting of several functions. So far the last attribute points to a function, and the function get is used to extract value from data model.

clipboard1.png

clipboard2.png

2. And you have already noticed that although we do not explicitly specify event handler for input field, however it can still react to our input. Why? Angular framework has registered a listener on input event for us. We will go through the implementation of listener later. So now for the input element, we have one watcher and one listener.

clipboard1.png

3. Again a second watcher is created for text node, “Hello {{name}}". So now we have two watchers created ans stored in watchers array of current scope object.

clipboard1.png

4. Still in bootstrap phase, the function $apply of scope is called.

clipboard1.png

Within this function, there is a DO loop to handle all watcher object of current scope one by one:

clipboard2.png

Since I haven't typed anything in input field, I have only the static text "Hello" to be displayed:

clipboard3.png

clipboard4.png

Before line 5624 is executed:

clipboard1.png

After executed:

clipboard2.png

So far, the initial page display logic is introduced.

 

How the value from Input field can flow to Hello text field

 

Now I have typed "A" in input field:

clipboard3.png


Since I have explained previously that Angular has registered a listener for input field, so now it is called:

clipboard1.png

In this event handler, scope.$apply will be called.

clipboard2.png

You still remember the fact that all watchers will be handled within scope.$apply? This time, the input character "A" is extracted from input field as model:

clipboard1.png

clipboard2.png

Since now the template "Hello {{name}}" has already been filled with actual value, it is ready to render it:

clipboard3.png

After line 5624 is executed, you will see "Hello A" in UI:

clipboard4.png

clipboard5.png


Compare Controller mechanism: SAPUI5 and Angular

$
0
0

Let's first refresh our memory on SAPUI5 controller. I have a simple xml view which only contains a button:

 

<core:View xmlns:core="sap.ui.core" xmlns:common="sap.ui.commons" controllerName="buttontutorial.view.simple"><common:Button text="Jerry" id="jerryButton"/></core:View>

 

And a simple controller:

 

sap.ui.define(["sap/ui/core/mvc/Controller"], function(Controller){
"use strict";
return Controller.extend("buttontutorial.view.simple",{
onInit : function() {  debugger;
}  });}
);

 

due to the attribute controllerName="buttontutorial.view.simple" in XML view, the controller instance is created and connect with XML view instance by UI5 framework:

clipboard1.png

And we can use JavaScript code in console to list the larget number of attributes belonging to created controller instance:

 

for( var name in this ) { console.log("attribute: " + name + " value: " + this[name]);}

clipboard2.png

Or you can simply type "this." in console, and see there are lots of methods available for controller instance:

clipboard3.png


For example, byId method of controller instance is widely used, if you type this.byId in console, you can see its implementation just delegates the call to this.oView.byId.

clipboard4.png

This makes sense since every controller instance holds a reference to its host view via oView, and the connection between controller and its view is established in function connectToView:

clipboard5.png

Angular Controller

 

You can play with the sample Angular application from this url.

clipboard7.png

It consists of 31 lines of source code:

<html ng-app>  <head>    <meta charset="utf-8">    <title>Angular.js Example</title>    <script src="angular/angular.js"></script>    <script>      function NameCtrl($scope){        $scope.names = ['ABAP', 'Java'];        $scope.addName = function() {          if( $scope.names.indexOf($scope.enteredName) != -1){            alert("duplicate key is not allowed in list: " + $scope.enteredName);            $scope.enteredName = '';            return;          }          $scope.names.push($scope.enteredName);          $scope.enteredName = '';        };    }    </script>  </head>  <body ng-controller="NameCtrl">    <ul>      <li ng-repeat="name in names">{{name}}      </li>    </ul>    <form ng-submit="addName()">      <input type="text" ng-model="enteredName">      <input type="submit" value="add">    </form>  </body></html>

 

When you type a new language in input field and click "Add" button, the language will be added into list above:

clipboard8.png

 


Let me first briefly introduce the idea of source code, then I will go through with each point in detail.

clipboard9.png

1. controller instance initialization

 

During Angular bootstrap phase, due to this line of source code in html, <body ng-controller="NameCtrl">, Angular will create a new controller instance in line 5327. You can consider $controller as a factory function.

clipboard1.png


Let's have a look at the content of argument locals for factory function:

clipboard2.png

The most important attribute is $scope, which is passed into function NameCtrl defined by us:

clipboard1.png

Once our application code is executed, controller instance is created. However, after checking it in Chrome, I found it is just a dummy instance without any important attribute. Instead, the data model and addName function are appended and available in current scope:

clipboard2.png

2. How addFunction available in scope object will be called when add button is called

 

Based on study result in step1, the addName function is located in scope object. My question is, when I press add button, why is it called?

In previous blog Compare Event handling mechanism: SAPUI5 and Angular, I already introduced that Angular does event handler registration automatically for us, as long as it detects the directive like this:

<form ng-submit="addName()">.


Actually I have made modifications on angular.js, adding more trace so that you can easily find where Angular achieves event registration under the hood:

clipboard1.png

clipboard2.png

clipboard3.png

So it is called as expected:

clipboard4.png

Summary

 

UI5 controller instance has a large number of useful functions available to use, and for Angular, controller instance is just a dummy one: data model and event handler function are located in scope object instead.

Creating a Fiori OVP Application with CDS view annotations - Part 1

$
0
0

In this blog I will provide some examples that demonstrate how CDS view annotations can be used to generate a Fiori Overview Page (OVP) Application.  An OVP application consists of an Application Header, a Smart Filter and one or many Cards.  The Cards can be used to show data in many forms such as tables and charts.  The CDS view UI annotations are how we define what is shown in the card.  For an overview of OVP see

 

User Interface Add-On for SAP NetWeaver - SAP Library

 

To be able to create and run the examples you will need the following:

 

  • Netweaver 7.5 System
  • Eclipse Mars or Juno
  • Web IDE account

 

We will also use data from the demo/testing Enterprise Procurement Modal(EPM).  If you have no data existing for the model you can use transaction code SEPM_DG to generate data.  For more information regarding EPM see


The NetWeaver Enterprise Procurement Model - An Introduction


We will start by creating a new CDS view in Eclipse.  If you haven't setup eclipse yet you can follow Step 1: Installation of Eclipse and its ADT plugin in Simmaco's Smart Template blog


How to create Smart Templates annotations within CDS views - part 1



CREATING THE CDS VIEW


If you haven't already done so, you will first need to add a new ABAP system.  This can be done in the ABAP perspective by choosing the menu option File -> New -> Other and searching for ABAP Project.  Either choose your system from the list or provide the necessary connection details.


With your system added choose New - > Other.  In the search input type ddl and choose DDL Source under ABAP


Screen Shot 2016-06-14 at 9.08.26 AM.png


After choosing Next provide the Project, Package, Name and Description details and choose NextScreen Shot 2016-06-14 at 9.10.31 AM.png


Provide any Transport Details if necessary and choose Next.  For Templates, we can use the Define View template.  Choose Finish to complete the process.Screen Shot 2016-06-14 at 9.12.54 AM.png


In the generated view set the values

  • sql_view_name: zovpdemo
  • data_source_name: sepm_cds_sales_order as so

 

and use the auto complete (CTRL + space) option Insert All Elements to add all of the columns.  This should result in

 

@AbapCatalog.sqlViewName: 'zovpdemo'
@AbapCatalog.compiler.compareFilter: true
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'OVP Demo App'
define view Z_Ovp_Demo as select from sepm_cds_sales_order as so {
so.sales_order_key,
so.sales_order_id,
so.created_by,
so.created_at,
so.changed_by,
so.changed_at,
so.note_guid,
so.currency_code,
so.gross_amount,
so.net_amount,
so.tax_amount,
so.lifecycle_status,
so.billing_status,
so.delivery_status,
so.buyer_guid,
 /* Associations */
so.customer,
so.items
}

 

After saving and activating, you can verify that the view is working correctly by right clicking on the view and choosing Open Width -> Data Preview.



ADDING SMART FILTER SEARCH FIELDS


The annotation @UI.selectionField can be used to mark fields as global filters for the OVP application.  For the purposes of this example we will mark the sales order id field as well as the customer.company_name with this annotation to allow searching on it.  It is also necessary to identify a field as a key which we will assign to the sales_order_key field.  The result of these changes yields

 

@AbapCatalog.sqlViewName: 'zovpdemo'
@AbapCatalog.compiler.compareFilter: true
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'OVP Demo App'
@OData.publish: true
define view Z_Ovp_Demo as select from sepm_cds_sales_order as so {
key so.sales_order_key,@UI.selectionField: [{ position: 10 }]
so.sales_order_id,
so.created_by,
so.created_at,
so.changed_by,
so.changed_at,
so.note_guid,
so.currency_code,
so.gross_amount,
so.net_amount,
so.tax_amount,
so.lifecycle_status,
so.billing_status,
so.delivery_status,
so.buyer_guid,
 /* Associations */
so.customer,
so.items
@UI.selectionField: [ { position: 20 } ]
so.customer.company_name
}

 

Within the selectionField annotation we assigned a position, this can be used to position fields in the UI when multiple are used.  We also added the annotation

 

@OData.publish: true

 

which is necessary to publish the CDS view as an odata service.

 


PUBLISHING THE SERVICE

 

After saving and activating the change you will notice a little bubble nice to the publish annotation.  This will give us access to the service url but it's usually required that the service is first added in the ABAP system.

 

To do so, open the t-code /n/iwfnd/maint_service and press the Add Service button.  In the Add Selected Services screen provide a System Alias, in my case I will use LOCAL, and then press the Get Services button.  The Technical Service Name field,  Z_Ovp_Demo, can be provided to filter the results if necessary.  Selecting the service should result in the Add Service dialog appearing.  Here you can assign it a package and choose the enter button to complete the process.

 

Screen Shot 2016-06-15 at 12.30.06 PM.png

 

Pressing the enter button should confirm your process and inform you that the service was created successfully.

 

 

GENERATING AN APPLICATION IN SAP WEB IDE

 

This step will require that you have a connection to your backend system setup in the HANA Cloud Platform for use with SAP Web IDE.  If you haven't done so please see

 

Setup your SAP Web IDE on HANA Cloud Platform Part 1

 

In SAP Web IDE choose the menu option File -> New -> Project From Template.  Choose the template Overview Page Application and choose Next.  Provide a name such as OVP_Demo and choose Next.  Using the Service Catalog Source select your system and then search for your service, Z_Ovp_Demo and choose Next.

 

In the Annotation Selection step the annotation file should appear automatically.  If it does not choose the option Add Annotation Files and choose the option Annotation URL.  Select your system and provide the url, correcting the TechnicalName if necessary

 

/sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Annotations(TechnicalName='Z_OVP_DEMO_CDS_VAN',Version='0001')/$value

 

Choose the Next button and then provide the values as shown.

 

Screen Shot 2016-06-15 at 4.05.38 PM.png

 

Press Finish to complete the template workflow.

 


A SMALL ADJUSTMENT

 

At the time of creating this blog I had to make the following adjustment due to some issues presented by the template.  Verify and adjust accordingly if this issue is presented in your generated app.  Open the neo-app.json and verify that your backend destination route path is as follows, making sure that if the path and entryPath are set as /sap/opu/odata/sap/Z_OVP_DEMO_CDS, both are changed to /sap/opu/odata


{      "path": "/sap/opu/odata",      "target": {        "type": "destination",        "name": "UIA_Virtual_000",        "entryPath": "/sap/opu/odata"      },      "description": "UIA_Virtual_000"    },


RUNNING THE APP

 

Select the Component.js and Run the app.  After pressing the filter icon your app should resemble

 

Screen Shot 2016-06-16 at 2.02.32 PM.png

 

In the next part we will add some additional annotations that we will then use to display some cards in the apps.  See part two at

 

Creating a Fiori OVP Application with CDS view annotations - Part 2

 

 

RESOURCES

 

ABAP CDS - SAP Annotations - ABAP Keyword Documentation

 

About ABAP Programming Model for SAP Fiori - SAP Library

Creating a Fiori OVP Application with CDS view annotations - Part 2

$
0
0

In this blog we will add some OVP Cards to the application we created in the previous blog.  If you haven't already done so, please review part 1 of the blog series which can be found at

 

Creating a Fiori OVP Application with CDS view annotations - Part 1

 


ADDING CARDS TO THE OVP APPLICATION


TABLE CARD

 

For the first card we will define the properties necessary to display a Table card.  This card presents three fields in a table with the first two columns showing the first two DataFields and the third column shows either the first existing DataPoint or the third DataField.  The annotation @UI.lineItem is used to define DataFields.  Within this annotation we can specify the position to be shown, a qualifier which allows us to identify an associated set of values, and a label.  With this knowledge lets start with a basic example by specifying lineItems for the sales_order_id, customer.company_name and the net_amount.  Notice how the position defers from the order of the fields.  We will use the qualifier in the OVP application when we define the associated card.  Save and activate the changes.

 

@AbapCatalog.sqlViewName: 'zovpdemo'
@AbapCatalog.compiler.compareFilter: true
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'OVP Demo App'
@OData.publish: true
define view Z_Ovp_Demo as select from sepm_cds_sales_order as so {
key so.sales_order_key,
 @UI.selectionField: [ { position: 10 } ]
 @UI.lineItem: { position: 10, qualifier:'ordOverView', label: 'Sales Order'  }
so.sales_order_id,
so.created_by,
so.created_at,
so.changed_by,
so.changed_at,
so.note_guid,
so.currency_code,
so.gross_amount,
 @UI.lineItem:  { position: 30, qualifier:'ordOverView', label: 'Net Amount'}
so.net_amount,
so.tax_amount,
so.lifecycle_status,
so.billing_status,
so.delivery_status,
so.buyer_guid,
 /* Associations */
so.customer,
so.items,
 @UI.selectionField: [ { position: 20 } ]
 @UI.lineItem:  { position: 20, qualifier:'ordOverView', label: 'Customer' }
so.customer.company_name
}

 

Now in Web IDE right click on the project and select New -> Card.  Choose the datasource as shown and press Next.

 

Screen Shot 2016-06-16 at 10.27.16 AM.png

Choose the Table Card and then press Next.

 

To define the card view provide the values as shown, making sure to include the qualifier ordOverView that we defined in the CDS view in the annotation path.

 

com.sap.vocabularies.UI.v1.LineItem#ordOverView

 

Press Next and Finish to complete the process.

 

Screen Shot 2016-06-16 at 3.08.37 PM.png

 

Selecting the Component.js file and choosing Run should result.

 

Screen Shot 2016-06-16 at 3.13.38 PM.png

 

Take note that using the smart filter can be used to filter out the displayed results.

 


TARGET VALUES

 

Taking this a step further, we can also use the @UI.lineItem to specify a DataPoint.  Notice in the example below the @UI.lineItem defined for net_amount specifies a type of #AS_DATAPOINT.  In addition to this we have now defined the annotation @UI.dataPoint on the net_amount field.  Using the targetValue as well as the criticalityCalculation we can now color code the values shown in the table as they fit in the defined ranges.

 

@AbapCatalog.sqlViewName: 'zovpdemo'
@AbapCatalog.compiler.compareFilter: true
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'OVP Demo App'
@OData.publish: true
define view Z_Ovp_Demo as select from sepm_cds_sales_order as so {
key so.sales_order_key,
 @UI.selectionField: [ { position: 10 } ]
 @Consumption.semanticObject: 'Action'
 @UI.lineItem: { position: 10, qualifier:'ordOverView', label: 'Sales Order'}
so.sales_order_id,
so.created_by,
so.created_at,
so.changed_by,
so.changed_at,
so.note_guid,
so.currency_code,
so.gross_amount,
 @UI.dataPoint: {title: 'Net Amt',      criticalityCalculation: {    improvementDirection: #TARGET,    deviationRangeLowValue: 100,    toleranceRangeLowValue: 400,    toleranceRangeHighValue: 800,    deviationRangeHighValue: 1200    }}
 @UI.lineItem:  { position: 30, qualifier:'ordOverView', label: 'Net Amount', type: #AS_DATAPOINT}
so.net_amount,
so.tax_amount,
so.lifecycle_status,
so.billing_status,
so.delivery_status,
so.buyer_guid,
 /* Associations */
so.customer,
so.items,
@UI.selectionField: [ { position: 20 } ]@UI.lineItem:  { position: 20, qualifier:'ordOverView', label: 'Customer' }
so.customer.company_name
}

 

After saving and activating the changes to the CDS view, refreshing the OVP Application should now show the card as

 

Screen Shot 2016-06-16 at 3.10.05 PM.png

 

First notice how the net amount label is now being taken from the label defined in the data point.  Also, each value shows with a color indicating how it fits in our range.  TECUM's Net Amt shows in orange which is the range between deviationRangeLowValue and toleranceRangeLowValue.

 


NAVIGATION

 

Now at this we may want to navigate to another app.  In addition to navigating to other sites we can also used intent based navigation which relies on semantic objects and actions.  Web IDE provides a launchpad sandbox which has a few apps that we can call using this approach, so lets navigate to one of these from the card.  The annotation @UI.lineItem supports an array of objects assignment in which we can define multiple assignment for the same field.  For this reason lets just add it to the sales_order_id annotations.  For this to work we will need to assign the type of #FOR_INTENT_BASED_NAVIGATION and also a semanticObjectAction which we can assign to toappnavsample to call the sample app.  In addition it is also required to define the Semantic Object using the annotation @Consumption.semanticObject which we will assign Action.

 

With these changes our view will now look like

 

@AbapCatalog.sqlViewName: 'zovpdemo'
@AbapCatalog.compiler.compareFilter: true
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'OVP Demo App'
@OData.publish: true
define view Z_Ovp_Demo as select from sepm_cds_sales_order as so {
key so.sales_order_key,
 @UI.selectionField: [ { position: 10 } ]
 @Consumption.semanticObject: 'Action'
 @UI.lineItem:  [{ label: 'salesOrd', qualifier:'ordOverView',type: #FOR_INTENT_BASED_NAVIGATION, semanticObjectAction: 'toappnavsample'},    { position: 10, qualifier:'ordOverView', label: 'Sales Order'}]
so.sales_order_id,
so.created_by,
so.created_at,
so.changed_by,
so.changed_at,
so.note_guid,
so.currency_code,
so.gross_amount,
 @UI.dataPoint: {title: 'Net Amt',      criticalityCalculation: {    improvementDirection: #TARGET,    deviationRangeLowValue: 100,    toleranceRangeLowValue: 400,    toleranceRangeHighValue: 800,    deviationRangeHighValue: 1200    }}
 @UI.lineItem:  { position: 30, qualifier:'ordOverView', label: 'Net Amount', type: #AS_DATAPOINT}
so.net_amount,
so.tax_amount,
so.lifecycle_status,
so.billing_status,
so.delivery_status,
so.buyer_guid,
 /* Associations */
so.customer,
so.items,
@UI.selectionField: [ { position: 20 } ]
@UI.lineItem:  { position: 20, qualifier:'ordOverView', label: 'Customer' }
so.customer.company_name
}

 

Save and activate the CDS view changes and then refresh the OVP application.  Choosing one of the items in the table should now navigate you to the sample app.  In my case I selected the entry for Telecomunicaciones Star which you can see in the passed parameters list as well as all of the other values.

 

Screen Shot 2016-06-16 at 2.56.18 PM.png

 


ADDING A LIST CARD


Normally for each card you add, you would define additional annotations in your CDS view and assign them a unique qualifier.  For the sake of simplicity, lets just reuse what we have already defined and define a List Card as shown


Screen Shot 2016-06-16 at 3.15.40 PM.png

 

and this will yield

 

Screen Shot 2016-06-16 at 3.17.49 PM.png

 

 

or it we choose the List Flavor Bar, which can be adjusted in the manifest.json which is part of the Web IDE project located under the webapp.

 

"card01": {  "model": "Z_OVP_DEMO_CDS",  "template": "sap.ovp.cards.list",  "settings": {  "title": "{{card01_title}}",  "category": "{{card01_category}}",  "subTitle": "{{card01_subTitle}}",  "entitySet": "Z_Ovp_Demo",  "listType": "condensed",  "listFlavor": "bar",  "sortBy": "changed_at",  "sortOrder": "descending",  "annotationPath": "com.sap.vocabularies.UI.v1.LineItem#ordOverView"  }

 

it would result in

Screen Shot 2016-06-16 at 3.20.33 PM.png

 

 

In the list flavor bar we are now only seeing two fields.  This view is expecting just one data field and two data points, so if we add another data point and set a value format each field as shown

 

 

@AbapCatalog.sqlViewName: 'zovpdemo'
@AbapCatalog.compiler.compareFilter: true
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'OVP Demo App'
@OData.publish: true
define view Z_Ovp_Demo as select from sepm_cds_sales_order as so {
key so.sales_order_key,
 @UI.selectionField: [ { position: 10 } ]
 @Consumption.semanticObject: 'Action'
 @UI.lineItem:  [{ label: 'salesOrd', qualifier:'ordOverView',type: #FOR_INTENT_BASED_NAVIGATION, semanticObjectAction: 'toappnavsample'},    { position: 10, qualifier:'ordOverView', label: 'Sales Order'}]
so.sales_order_id,
so.created_by,
so.created_at,
so.changed_by,
so.changed_at,
so.note_guid,
so.currency_code,
so.gross_amount,
 @UI.dataPoint: {title: 'Net Amt',      criticalityCalculation: {    improvementDirection: #TARGET,    deviationRangeLowValue: 100,    toleranceRangeLowValue: 400,    toleranceRangeHighValue: 800,    deviationRangeHighValue: 1200    },    valueFormat:{        numberOfFractionalDigits: 1    }}
@UI.lineItem:  { position: 30, qualifier:'ordOverView', label: 'Net Amount', type: #AS_DATAPOINT}
so.net_amount,
 @UI.dataPoint: {title: 'Tax Amt', valueFormat:{    numberOfFractionalDigits: 1
}}
 @UI.lineItem:  { position: 40, qualifier:'ordOverView', label: 'Tax Amount', type: #AS_DATAPOINT}
so.tax_amount,
so.lifecycle_status,
so.billing_status,
so.delivery_status,
so.buyer_guid,
 /* Associations */
so.customer,
so.items,
@UI.selectionField: [ { position: 20 } ]
@UI.lineItem:  { position: 20, qualifier:'ordOverView', label: 'Customer' }
so.customer.company_name
}

 

 

After adjusting the card01_title in the i18n properties to Net/Tax Sales Orders, this will yield

 

Screen Shot 2016-06-16 at 3.43.04 PM.png

 

 

Now if we wanted more fields we could change the List View to show in the list type extended which again we can just change in the manifest.json.

 

"card01": {  "model": "Z_OVP_DEMO_CDS",  "template": "sap.ovp.cards.list",  "settings": {  "title": "{{card01_title}}",  "category": "{{card01_category}}",  "subTitle": "{{card01_subTitle}}",  "entitySet": "Z_Ovp_Demo",  "listType": "extended",  "listFlavor": "bar",  "sortBy": "changed_at",  "sortOrder": "descending",  "annotationPath": "com.sap.vocabularies.UI.v1.LineItem#ordOverView"  }  }

 

 

The extended list card presents five fields, on the top left side it shows the first two data fields and on the bottom it present the first data point value in a bar and the second and third data point on the right side of the card if they exist.  To max out the fields shown we can add one additional field in our cds view.  This time we will add the lifecycle status field.  Modify your cds view as shown below and save and activate your changes.

 

@AbapCatalog.sqlViewName: 'zovpdemo'
@AbapCatalog.compiler.compareFilter: true
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'OVP Demo App'
@OData.publish: true
define view Z_Ovp_Demo as select from sepm_cds_sales_order as so {
key so.sales_order_key,
 @UI.selectionField: [ { position: 10 } ]
 @Consumption.semanticObject: 'Action'
 @UI.lineItem:  [{ label: 'salesOrd', qualifier:'ordOverView',type: #FOR_INTENT_BASED_NAVIGATION, semanticObjectAction: 'toappnavsample'},    { position: 10, qualifier:'ordOverView', label: 'Sales Order'}]
so.sales_order_id,
so.created_by,
so.created_at,
so.changed_by,
so.changed_at,
so.note_guid,
so.currency_code,
so.gross_amount,
 @UI.dataPoint: {title: 'Net Amt',      criticalityCalculation: {    improvementDirection: #TARGET,    deviationRangeLowValue: 100,    toleranceRangeLowValue: 400,    toleranceRangeHighValue: 800,    deviationRangeHighValue: 1200    },    valueFormat:{        numberOfFractionalDigits: 1    }}
@UI.lineItem:  { position: 30, qualifier:'ordOverView', label: 'Net Amount', type: #AS_DATAPOINT}
so.net_amount,
 @UI.dataPoint: {title: 'Tax Amt', valueFormat:{    numberOfFractionalDigits: 1
}}
 @UI.lineItem:  { position: 40, qualifier:'ordOverView', label: 'Tax Amount', type: #AS_DATAPOINT}
so.tax_amount,  @UI.dataPoint: {title: 'Lifecycle'}  
 @UI.lineItem:  { position: 50, qualifier:'ordOverView', label: 'Lifecycle', type: #AS_DATAPOINT}
so.lifecycle_status,
so.billing_status,
so.delivery_status,
so.buyer_guid,
 /* Associations */
so.customer,
so.items,
@UI.selectionField: [ { position: 20 } ]
@UI.lineItem:  { position: 20, qualifier:'ordOverView', label: 'Customer' }
so.customer.company_name
}

 

After refreshing the OVP application your should now see the additional fields showing.

 

Screen Shot 2016-06-16 at 3.59.05 PM.png

 

In the next blog we will explore the use of the Stack Card and a Chart Card.

 

Creating a Fiori OVP Application with CDS view annotations - Part 3

Creating a Fiori OVP Application with CDS view annotations - Part 3

$
0
0

In this blog we will add some additional OVP Cards to the application we created in the previous two blogs.  If you haven't already done so, please review the first two blogs of the series, which can be found at

 

Creating a Fiori OVP Application with CDS view annotations - Part 1

 

Creating a Fiori OVP Application with CDS view annotations - Part 2

 



ADDING A STACK CARD TO THE OVP APPLICATION

 

The stack card relies on the annotations defined by @UI.headerInfo, @UI.identification and @UI.fieldGroup. Using the headerInfo annotation allows us to define  a typeName which will be shown as the card header, as well as a title and header.  In addition you can also utilize the annotation @UI.headerInfo.imageUrl to display an image next to the title and description.  For the image, I will just hard card a value there, field mytestImg, due to the view not having a image field at this level.

 

These assignments will be as follows

 

@UI.headerInfo: {  typeNamePlural: 'Sales Orders',
imageUrl: 'mytestImg',  typeName: 'Sales Order',  title: {    label: 'Order ID',    value: 'sales_order_id'  },  description: {    label: 'Customer',    value: 'company_name'  }
}
define view Z_Ovp_Demo as select from sepm_cds_sales_order as so {
....
 '/resources/sap/ui/core/mimes/logo/sap_50x26.png' as mytestImg,

 

 

The stack card has two clickable areas.  The left side of the card, the white can be used to navigate to another app using semantic navigation.  Clicking the right side will open up the card view.  To assign the left side of the card's navigation point we will utilize the @UI.identification annotation as follows.  This will also display within each card with the position identifying the layout.

 

@UI.identification: {type: #FOR_INTENT_BASED_NAVIGATION, semanticObjectAction: 'toappnavsample2', label: 'Nav Sample', position: '10'}

 

We can also add another button in each card using the same annotation.  For this we can utilize the web_address field as the navigation path.

 


@UI.identification: {type: #WITH_URL, label: 'Customer Site', url: 'web_address', position: '20'}
so.customer.web_address

 

Finally, we can define a fieldGroup to display some additional data in each card, but to utilize this we will have to add some additional local annotations in our Web IDE OVP application.  We can do this by utilizing the Annotation Modeler of Web IDE, but lets first save and activate the CDS View.

 

@UI.fieldGroup: {groupLabel: 'Contact Details', label: 'Email', position: '10', qualifier: 'cusContact'}
so.customer.email_address,
@UI.fieldGroup: {groupLabel: 'Contact Details', label: 'Phone', position: '20', qualifier: 'cusContact'}
so.customer.phone_number,

 

The result of our changes necessary for our stack card should yield

 

@AbapCatalog.sqlViewName: 'zovpdemo'
@AbapCatalog.compiler.compareFilter: true
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'OVP Demo App'
@OData.publish: true
@UI.headerInfo: {  typeNamePlural: 'Sales Orders',
imageUrl: 'mytestImg',  typeName: 'Sales Order',  title: {    label: 'Order ID',    value: 'sales_order_id'  },  description: {    label: 'Customer',    value: 'company_name'  }
}
define view Z_Ovp_Demo as select from sepm_cds_sales_order as so {
key so.sales_order_key,
@UI.selectionField: [ { position: 10 } ]
@Consumption.semanticObject: 'Action'
@UI.identification: {type: #FOR_INTENT_BASED_NAVIGATION, semanticObjectAction: 'toappnavsample2', label: 'Nav Sample', position: '10'}
@UI.lineItem:  [{ label: 'salesOrd', qualifier:'ordOverView',type: #FOR_INTENT_BASED_NAVIGATION, semanticObjectAction: 'toappnavsample'},    { position: 10, qualifier:'ordOverView', label: 'Sales Order'}]
so.sales_order_id,
so.created_by,
so.created_at,
so.changed_by,
so.changed_at,
so.note_guid,
so.currency_code,
so.gross_amount,
@UI.dataPoint: {title: 'Net Amt',    criticalityCalculation: {    improvementDirection: #TARGET,    deviationRangeLowValue: 100,    toleranceRangeLowValue: 400,    toleranceRangeHighValue: 800,    deviationRangeHighValue: 1200    },    valueFormat:{        numberOfFractionalDigits: 1    }}
@UI.lineItem:  { position: 30, qualifier:'ordOverView', label: 'Net Amount', type: #AS_DATAPOINT}

so.net_amount,
@UI.dataPoint: {title: 'Tax Amt', valueFormat:{    numberOfFractionalDigits: 1
}}
@UI.lineItem:  { position: 40, qualifier:'ordOverView', label: 'Tax Amount', type: #AS_DATAPOINT}
so.tax_amount,
@UI.dataPoint: {title: 'Lifecycle'}
@UI.lineItem:  { position: 50, qualifier:'ordOverView', label: 'Lifecycle', type: #AS_DATAPOINT}
so.lifecycle_status,
so.billing_status,
so.delivery_status,
so.buyer_guid,
/* Associations */
so.customer,
so.items,
@UI.selectionField: [ { position: 20 } ]
@UI.lineItem:  { position: 20, qualifier:'ordOverView', label: 'Customer',  }
so.customer.company_name,
@UI.fieldGroup: {groupLabel: 'Contact Details', label: 'Email', position: '10', qualifier: 'cusContact'}
so.customer.email_address,
@UI.fieldGroup: {groupLabel: 'Contact Details', label: 'Phone', position: '20', qualifier: 'cusContact'}
so.customer.phone_number,
@UI.identification: {type: #WITH_URL, label: 'Customer Site', url: 'web_address', position: '20'}
so.customer.web_address
 '/resources/sap/ui/core/mimes/logo/sap_50x26.png' as mytestImg,
}


After saving and activating the CDS view changes we can now add the Stack Card to the application.  In your OVP Application add a Stack Card as shown

 

Screen Shot 2016-06-17 at 10.02.26 AM.png

 

 

This will yield the card

 

Screen Shot 2016-06-28 at 4.16.04 PM.png

 

The card has two clickable areas, the white area and also the blue area.  Clicking on the white area relies on the first @UI.identification and clicking on the blue area which opens the cards results in

 

Screen Shot 2016-06-29 at 2.55.03 PM.png

 

Choosing the Customer Site button will navigate us to the url provided by the field web_address and the Nav Sample button we take us to a predefined navigation sample app.

 

 

Adding the Field Group

 

To utilize the field group, it's necessary to add some additional annotations utilizing the Annotation Modeler.  To do this let's first right click on the Web IDE project's webapp folder and choose New -> folder and name it annotations.  Now right click on the annotations folder and choose New -> Annotation File and provide the values

 

Screen Shot 2016-06-28 at 4.22.04 PM.png

 

choose Next and Finish to complete the process.  In addition to creating the file, this process will add an additional entry in the manifest.json declaring a local annotation.  Right click on the new annotation file and choose Open With -> Annotation Modeler.  When the editor opens, choose the Annotate button next to the OData Entity Set.

 

Screen Shot 2016-06-29 at 2.58.02 PM.png

 

Select the node Local Annotations and then choose the add button under actions.

 

Screen Shot 2016-06-29 at 3.00.43 PM.png

 

Choose the annotation UI.Facets and choose OK.

 

Screen Shot 2016-06-28 at 4.26.04 PM.png

 

Now with the Facet annotation selected, choose the add button and add a ReferenceFacet to it.

 

Screen Shot 2016-06-28 at 4.27.26 PM.png

 

Finally assign the fieldGroup to the ReferenceFacet by setting the Annotation property to the FieldGroup Qualifier cusContact and Save your changes.Screen Shot 2016-06-28 at 4.29.05 PM.png

 

After refreshing your app your stack card should now look like

 

Screen Shot 2016-06-29 at 3.02.21 PM.png

 

 

 

ADDING A CHART CARD TO THE OVP APPLICATION


OVP applications have the ability to show numerous types of analytical charts.  In this example we will add a line chart, but the procedure is the same form any type of chart card.  CDS views provide the annotation @UI.chart which contain the majority of the information related to the card.  To adjust the displayed data we can also utilize the annotation @UI.presentationVariant, which allows grouping and sorting or data, and the annotation @UI.selectionVariant, which allows filtering of data.  Additionally, the annotation @UI.dataPoint is available to display a KPI value in the card header as well as the annotation @UI.identification to define a navigation path.


In our CDS view lets first define a line chart as follows in the view area.


@UI.chart:[{    qualifier: 'ordNetChart',    title:'Order Net Amount',    chartType: #LINE,    dimensions: ['sales_order_id'],    measures: ['net_amount', 'tax_amount'],    dimensionAttributes: {dimension: 'sales_order_id', role:#SERIES},    measureAttributes: [{measure: 'net_amount', role: #AXIS_1}, {measure: 'tax_amount', role: #AXIS_1}]
 }]

 

To limit the amount of data we see in our chart we can utilize the presentation variant annotation which we can use to sort the data in addition to limiting the results returned.  This is also a view level annotation so we can add it under our chart.

 

 

@UI.presentationVariant: [{qualifier: 'top5Changed', maxItems: '5',  sortOrder.by: 'changed_at', sortOrder.direction: #DESC }]n: #DESC }]

 

For the example we can define a static field myTarget which we used to display in the KPI header area using the @UI.dataPoint annotation.

 

 @UI.dataPoint:{title: 'Order Target Value', criticalityCalculation: {        improvementDirection: #MAXIMIZE,         toleranceRangeLowValue: 8000,         deviationRangeLowValue: 9000} }    10000.00 as myTarget,

 

 

The resulting CDS view should now be

 

@AbapCatalog.sqlViewName: 'zovpdemo'
@AbapCatalog.compiler.compareFilter: true
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'OVP Demo App'
@OData.publish: true
@UI.headerInfo: {  typeNamePlural: 'Sales Orders',  imageUrl: 'mytestImg',  typeName: 'Sales Order',  title: {    label: 'Order ID',    value: 'sales_order_id'  },  description: {    label: 'Customer',    value: 'company_name'  }
}
@UI.chart:[{    qualifier: 'ordNetChart',    title:'Order Net Amount',    chartType: #LINE,    dimensions: ['sales_order_id'],    measures: ['net_amount', 'tax_amount'],    dimensionAttributes: {dimension: 'sales_order_id', role:#SERIES},    measureAttributes: [{measure: 'net_amount', role: #AXIS_1}, {measure: 'tax_amount', role: #AXIS_1}]
 }]
@UI.presentationVariant: [{qualifier: 'top5Changed', maxItems: '5',  sortOrder.by: 'changed_at', sortOrder.direction: #DESC }]
define view Z_Ovp_Demo
as select from sepm_cds_sales_order as so
{
key so.sales_order_key,    @Consumption.semanticObject: 'Action'    @UI.identification: {type: #FOR_INTENT_BASED_NAVIGATION, semanticObjectAction: 'toappnavsample2', label: 'Nav Sample', position: '10'}    @UI.lineItem:  [{ label: 'salesOrd', qualifier:'ordOverView',type: #FOR_INTENT_BASED_NAVIGATION, semanticObjectAction: 'toappnavsample'},      { position: 10, qualifier:'ordOverView', label: 'Sales Order'}]    @UI.selectionField: [ { position: '10' } ]    so.sales_order_id,    so.created_by,    so.created_at,    so.changed_by,    so.changed_at,    so.note_guid,    so.currency_code,    '/resources/sap/ui/core/mimes/logo/sap_50x26.png' as mytestImg,    @UI.dataPoint:{title: 'For Charts', referencePeriod.end: 'created_at', criticalityCalculation: {        improvementDirection: #MAXIMIZE,         toleranceRangeLowValue: 1000,         deviationRangeLowValue: 4000}}    so.gross_amount,    @UI.dataPoint:{title: 'Order Target Value', criticalityCalculation: {        improvementDirection: #MAXIMIZE,         toleranceRangeLowValue: 8000,         deviationRangeLowValue: 9000} }    10000.00 as myTarget,    @UI.dataPoint: {title: 'Net Amt',       criticalityCalculation: {        improvementDirection: #TARGET,        deviationRangeLowValue: 100,        toleranceRangeLowValue: 400,        toleranceRangeHighValue: 800,        deviationRangeHighValue: 1200        },        valueFormat:{          numberOfFractionalDigits: 1        }}    @UI.lineItem:  [{ position: 30, qualifier:'ordOverView', label: 'Net Amount',  type: #AS_DATAPOINT},{qualifier: 'chartLineItem'}]    so.net_amount,    @UI.dataPoint: {title: 'Tax Amt', valueFormat:{      numberOfFractionalDigits: 1    }}    @UI.lineItem:  { position: 40, qualifier:'ordOverView', label: 'Tax Amount', type: #AS_DATAPOINT}    so.tax_amount,    @UI.dataPoint: {title: 'Lifecycle'}    @UI.lineItem:  { position: 50, qualifier:'ordOverView', label: 'Lifecycle', type: #AS_DATAPOINT}    so.lifecycle_status,    so.billing_status,    so.delivery_status,    so.buyer_guid,    /* Associations */    so.customer,    so.items,    @UI.lineItem:  { position: 20, qualifier:'ordOverView', label: 'Customer' }    @UI.selectionField: [ { position: '20' } ]    @UI.identification: [{label: 'Customer', position: '10'}]    so.customer.company_name,    @UI.fieldGroup: {groupLabel: 'Contact Details', label: 'Email', position: '10', qualifier: 'cusContact'}    so.customer.email_address,    @UI.fieldGroup: {groupLabel: 'Contact Details', label: 'Phone', position: '20', qualifier: 'cusContact'}    so.customer.phone_number,    @UI.identification: {type: #WITH_URL, label: 'Customer Site', url: 'web_address', position: '20'}    so.customer.web_address
}                                                                                 

 

After saving and activating the CDS view add another card to define a Line Chart.  Define the card as

 

Screen Shot 2016-07-05 at 1.34.56 PM.png

 

The should result in the card

 

Screen Shot 2016-07-05 at 1.39.01 PM.png

SAP Web IDE local development with SAP HANA On Premise

$
0
0

In this post I will show how to develop a basic SAP UI5 app locally using OData service located in a SAP HANA On Premise server. I will also show how to take the app to the same SAP HANA server. My OS is windows, but you can easy make analogous steps if you are woking in MacOSx.

 

 

Getting SAP Web IDE

 

First, you will need a SAP Web IDE local installation.

You can download it from this site.

It is really easy to install this new version. You only have to extract the downloaded zip file and execute the orion server.

Details are in this page.

 

After you have a SAP Web IDE local installation, you better create a user account in your local server in order you can make SAP UI5 applications.

 

 

The SAP Web IDE project

 

To make a new project you can follow the File > New > Project from Template route.

 

newProject.png

 

 

In this case, I will use SAPUI5 Application with the SAPUI5 Innovation (Recommended) version.

 

template.png

 

 

For this project I choose tileMetrics as the project name and demo.project as the namespace. Also, I will work with XML views and this first view will be named Main.

 

This is what you have when you run the app for this first time.

empty.png

 

 

On the root directory (tileMetrics in my case) I will create another folder: webservice.

In this folder I'm going to create the .xsodata file for de OData webservice: demo.xsodata

 

service  {   "MDMUSER"."V_METRICS"   as "DemoNumber"   key  ("CONCEPT");
}

MDMUSER.V_METRICS is a view that runs queries on tables to get numbers like the total count of rows, the total count when a column is null, etc.

 

For now, this is our site structure:

structure.png

 

 

We also need the .xsaccess and .xsapp file next to .project.json and neo.app.json.

xsaccess.png

 

 

For now, we need to define a project in SAP HANA wih the same structure than this SAP Web IDE project.

 

hana.png

 

Destination file

 

Depending of your extraction route of the zip file that you downloaded first, you have a similar route like this: C:\SAPWebIDE\eclipse\config_master\service.destinations\destinations.

This route exists only after you create a user for SAP Web IDE.

 

In that directory, you will crete a file with no extension with the connection information to you SAP HANA server.

In my case, this is the content:

 

Description=Webservices demonstration
Name=demo
Type=HTTP
URL=http://XX.XX.X.XX:XXXX
ProxyType=Internet
Authentication=BasicAuthentication
WebIDEEnabled=true
WebIDESystem=demo
WebIDEUsage=odata_gen

 

 

 

Back to the SAP Web IDE project

 

We need to configure this OData service.

With the right click on the root directory, you follow the route: New > OData Service

 

service.png

 

 

This image shows you which are the parameters that you need to set and finish the configuration:

 

odata.png

 

 

Then you need to relate this OData service with you project. So, inside webapp directory you will find manifest.json. In that file you will have a dataSources section, that reflects the configuration that you have already done.

 

dataSources.png

 

 

In manifest.json you can also find models section. We need to add the dataSource in this models section.

 

models.png

 

Till now, the app still showing nothing, but this is about to change now.

You need to modify the Main view to paint a tile with the numbers that we're extracting from the webservice.

 

Inside the content tag of the Main view, write something like this:

 

<TileContainer tiles="{demo>/DemoNumber}" id="demoContainer" allowAdd="false" editable="false">                <StandardTile number="{demo>TOTAL}" title="{demo>CONCEPT}" numberUnit="rows" info="SAP" icon="sap-icon://sys-monitor"/>            </TileContainer>

 

view.png

 

 

If you run the app, now you will see this:

 

app.png

 

You can try changing the window size and see what happens.

 

 

Moving to SAP HANA server

 

 

In order to move this simple app to the SAP HANA server, we need to add a new dataSource in the manifest.json file and change the model.

 

hanaDataSource.png

 

demoHanaServer.png

 

Now you can export tileMetics directory and put it in you SAP HANA project.

I always modify the SAP UI5 CORE resources directory parameter to: https://openui5.hana.ondemand.com/resources/sap-ui-core.js in the index.html file.

 

 

SAP UI5 app on SAP HANA server On Premise:

 

hanaApp.png

 

Please let me know if you have any questions.

Check versions of SAPUI5 on ABAP and JAVA stacks

$
0
0

While checking the version of SAPUI5 libraries on the ABAP stack is widely known, I could not find anything to check the version on the JAVA stack.

 

One way to see the version is to go to anything UI5 in portal and press CTRL+SHIFT+ALT+P.  This will provide you an SAPUI5 version, only for the stack you indicated the libraries should be consumed from.

 

For finding the version on the ABAP stack, there are several posts letting you know you can use the following URL:

 

http://<host>:<port>/sap/public/bc/ui5_ui5/


But for the JAVA stack, I had to open an incident to find out there is a URL for that also:


http://<host>:<port>/sapui5/resources/sap/m/.library


We have found that while we consume the libraries from the ABAP stack, we build our theme for the FFP on the JAVA stack and if the versions are not kept in sync, we see inconsistencies in the theme.

Tips for beginners to learn SAPUI5

$
0
0


                When I started learning sapui5  in the beginning I did not know how to approach this technology . Where to start with and what are the skill

                         I  need to be strong in to Learn this course so here I am sharing my experience of UI5 journey  Hope it will be helpful.

 

STEP :1

                Try to Learn HTML,JAVASCRIPT,CSS  it will be very helpful. There  are few online platform to learn these code like W3 schools , Code academy, JS garden etc.

                        I will recommend W3 schools for HTML  and CSS . For JAVASCRIPT code academy is a better option.

               As  sapui5 is a new technology you will find very few good book .

              Getting started with sapui5is a good book from SAP Press which is a very good  to Learn UI5.

             To be good at any user experience technology you need to enhance user design thinking capability for that I will recommend  to go through various                              website and several course is available to give you a fair idea about it.

 

STEP :2

                   SAP provides a Tool called BUILD which involves only drag and drop no coding is involved so you can design  and visualize the APP you are                          going to   build.

                   https://www.experiencesplash.com/splashapp/   this is the link to BUILD.

                   After going through all the above topic  its time to do some coding.

                  Eclipse is a good  platform to start UI5 coding . It is very flexible also give you adequate scope to do good amount of coding.

                  SAPUI5 SDK - Demo Kit is a website contains all   the tutorial segregated by topics you can refer and learn UI5.

                  It also contains many   control and its code  which will make coding Easy.

                  SAPUI5 Explored  is there in SAPUI5 DEMO Kit which  contains lot of control here the view is in XML Language   so while coding in XML it is quit                       useful.

                 Now you know coding language  , you have the idea of design and you have different controls to help you

                 So you can create as many kind of App as possible.

                 SCN(SAP community Network) , Stack Overflow are some good website where you can query and can get your doubt resolved.

STEP:3 

              SAP provides several course on its portal openSap which is a good way to Learn thinks about SAP.  Here is the                                URL  open.sap.com/courses .Build Your Own SAP Fiori App in the Cloud  ,

                  Developing Web Apps in Sap ui5    are two good course  Related to SAPUI5 you should    Learn.

                   Now its time to switch from Eclipse To SAP Web IDE a cloud based Platform To code and your  project will be saved in cloud so you can access                      it anywhere.

                    SAP Web IDE provides different Template to make UI5 easier.

                    You can contribute to SCN by  actively participating and can Learn New things about SAP UI5.

 

 

          WelCome To SAPUI5


            Regards

            Ansuman 

 

 

 

 

 

 

 

 




Latest Images