Introduction:
In the previous article Step by Step: Visualization using XS-based Application, we exposed the data from the calculation view using an XSJS service. In this article we'll discuss using OData service.
What is OData? An open protocol to allow the creation and consumption of queryable and interoperableRESTful APIs in a simple and standard way. For more details on OData, please refer Home | OData - The Protocol for REST APIs
Here is an article REST your Models on SAP HANA XS by Werner Steyn with step by step instructions on creating an ODATA service and its consumption.
So to reproduce the same visualization (as in the previous article), please follow the steps as below.
Step 5: Create an OData Service to read from Calculation View
We’ll create an OData Service Definition Language file referencing our Calculation view. (It need not be a HANA information models. Technically it can be any data objects like Table, Views or any HANA model)
Let’s create a file “CaSalesOrder.xsodata” under the “odata” folder. To do so, select the "odata" folder, Right Click: New -> File.
Use the following code and activate the “CasalesOrder.xsodata".
service { "myapp::CA_SALES_ORDER" // Table or View as "salesOrder" // Optional entity alias key ("ORDERID","PRODUCTID") // Optional key relevant for view aggregates always; // Additional aggregation per column are MIN, MAX, AVG }
Now let's test our OData service
Now append the following parameters to the URL (?$top=10&$select=CUSTOMERID,NETSALES,COST) to see the data. In this example, we are requesting TOP 10 NETSALES, COST by CUSTOMERID
To generate an JSON based output to append &$format=JSON) to the URL.
Please note, the data records are bundled with path of "d/results" (vs. our previous example), so we have to make appropriate changes to our code.
Step 6: Create a view (SAPUI5 Table) to visualize data
We’ll create a view “odataTable.view.js” under "views" folder to consume the data from our OData service. To do so, select the “views” folder, Right Click: New -> File.
Use the following code and activate the “odataTable.view.js”
sap.ui.jsview("views.odataTable", { /** Specifies the Controller belonging to this View. * In the case that it is not implemented, or that "null" is returned, this View does not have a Controller. * @memberOf views.odataTable */ getControllerName : function() { return null; }, /** Is initially called once after the Controller has been instantiated. It is the place where the UI is constructed. * Since the Controller is given to this method, its event handlers can be attached right away. * @memberOf views.odataTable */ createContent : function(oController) { var oLayout = new sap.ui.commons.layout.MatrixLayout({width:"100%"}); var url = "odata/CaSalesOrder.xsodata/"; var oModel = new sap.ui.model.odata.ODataModel(url, false); var oControl; this.oSHTable = new sap.ui.table.Table("soTable",{ visibleRowCount: 10, }); this.oSHTable.setTitle("SALES_ORDERS"); //Table Column Definitions var oMeta = oModel.getServiceMetadata(); var oControl; oControl = new sap.ui.commons.TextView().bindProperty("text","CUSTOMERID"); this.oSHTable.addColumn(new sap.ui.table.Column({label:new sap.ui.commons.Label({text: "CUSTOMERID"}), template: oControl, sortProperty: "CUSTOMERID", filterProperty: "CUSTOMERID", filterOperator: sap.ui.model.FilterOperator.EQ, flexible: true })); oControl = new sap.ui.commons.TextView().bindProperty("text", "NETSALES"); oControl.setTextAlign("End"); this.oSHTable.addColumn(new sap.ui.table.Column({label:new sap.ui.commons.Label({text: "NETSALES"}), template: oControl, sortProperty: "NETSALES", filterProperty: "NETSALES", hAlign: sap.ui.commons.layout.HAlign.End})); oControl = new sap.ui.commons.TextView().bindProperty("text","COST"); oControl.setTextAlign("End"); this.oSHTable.addColumn(new sap.ui.table.Column({label:new sap.ui.commons.Label({text: "COST"}), template: oControl, sortProperty: "COST", filterProperty: "COST", hAlign: sap.ui.commons.layout.HAlign.End})); this.oSHTable.setModel(oModel); this.oSHTable.bindRows({ path: "/salesOrder" }); this.oSHTable.setTitle("Sales Orders"); oLayout.createRow(this.oSHTable); return oLayout; } });
Please note the code above is very similar to the code we've used in Step 6 of the previous article. The changes are in line 18, we are using sap.ui.model.odata.ODataModel instead of sap.ui.model.json.JSONModel. Also we are using fields CUSTOMERID, NETSALES, COST (instead of customerid, netsales, cost) as the name of the fields exposed in the OData model.
Save and activate the “odataTable.view.js” view
Now let's create a file “odata_index.html” to invoke the new view "odataTable". Use the following code and activate the “odata_index.html”
<html><head> <meta http-equiv="X-UA-Compatible" content="IE=edge"><script src="/sap/ui5/1/resources/sap-ui-core.js" id="sap-ui-bootstrap" data-sap-ui-libs="sap.ui.ux3,sap.ui.commons,sap.ui.table,sap.viz" data-sap-ui-theme="sap_goldreflection"> </script><script> sap.ui.localResources("views"); var view = sap.ui.view({id:"odataTable", viewName:"views.odataTable", type:sap.ui.core.mvc.ViewType.JS}); view.placeAt("contentTable");</script></head><body class="sapUiBody" role="application"> <div id="contentTable"></div> <div id="contentGraph"></div> </body></html>
There is one significant big advantage with this approach. The table view is displaying the first 10 records from the OData view and by clicking the highlighted section, you can read the next set of 10 records and so on. Since only 10 records are being fetched at any time, it is extremely fast and responsive.
Step 7: Create SAPUI5 View (Bar Chart) to visualize data
Now let’s create another view “odataGraph.view.js” to display the Bar Chart from the OData service.
So let’s create a file “odataGraph.view.js” under the “views” folder. Use the following code and activate the “odataGraph.view.js”
sap.ui.jsview("views.odataGraph", { /** Specifies the Controller belonging to this View. * In the case that it is not implemented, or that "null" is returned, this View does not have a Controller. * @memberOf views.odataGraph */ getControllerName : function() { return null; }, /** Is initially called once after the Controller has been instantiated. It is the place where the UI is constructed. * Since the Controller is given to this method, its event handlers can be attached right away. * @memberOf views.odataGraph */ createContent : function(oController) { var topSalesCustomerBarChart = new sap.viz.ui5.Bar("topTenCustomerBarChart", { width : "100%", height : "50%", xAxis: { title: { visible: true, text : "EUR" } }, title : { visible : true, text : 'Top Ten Customers by Sales' } , interaction: new sap.viz.ui5.types.controller.Interaction({ selectability: new sap.viz.ui5.types.controller.Interaction_selectability({ mode: sap.viz.ui5.types.controller.Interaction_selectability_mode.single}) }), dataset : topSalesDataset = new sap.viz.ui5.data.FlattenedDataset({ // a Bar Chart requires exactly one dimension (x-axis) dimensions : [ { axis : 1, // must be one for the x-axis, 2 for y-axis name : 'Customer', value : "{CUSTOMERID}" }], // it can show multiple measures, each results in a new set of bars // in a new color measures : [ { name : 'Net Sales', // 'name' is used as label in the Legend value : '{NETSALES}' // 'value' defines the binding for the }, { name : 'Cost', // 'name' is used as label in the Legend value : '{COST}' // 'value' defines the binding for the } ], // 'data' is used to bind the whole data collection that is to be // displayed in the chart data : { path : "/d/results" } }) }); var url = "odata/CaSalesOrder.xsodata/salesOrder?$top=10&$select=CUSTOMERID,NETSALES,COST&$format=json"; var salesModel = new sap.ui.model.json.JSONModel(); salesModel.loadData(url); topSalesCustomerBarChart.setModel(salesModel); return topSalesCustomerBarChart; } });
There are two notable changes here.
- We are using the new path "/d/results" (instead of "/") as explained in step 5.
- We are calling the OData service using the URL in line 57
Save and activate the “odataGraph.view.js” view
Now let's update the "odata_index.html" to invoke the "odataGraph" view. Use the following code and activate the “odata_index.html”.
<html><head> <meta http-equiv="X-UA-Compatible" content="IE=edge"><script src="/sap/ui5/1/resources/sap-ui-core.js" id="sap-ui-bootstrap" data-sap-ui-libs="sap.ui.ux3,sap.ui.commons,sap.ui.table,sap.viz" data-sap-ui-theme="sap_goldreflection"> </script><script> sap.ui.localResources("views"); var view = sap.ui.view({id:"odataTable", viewName:"views.odataTable", type:sap.ui.core.mvc.ViewType.JS}); view.placeAt("contentTable"); var view = sap.ui.view({id:"odataGraph", viewName:"views.odataGraph", type:sap.ui.core.mvc.ViewType.JS}); view.placeAt("contentGraph"); </script></head><body class="sapUiBody" role="application"> <div id="contentTable"></div> <div id="contentGraph"></div> </body></html>
Step 8: Dynamic Visualization using VizContainer
The changes are identical to the changes in Step 7. So go ahead try for yourself.
Step 9: Create a Dynamic Table View based on the OData service
This is a quick little enhancement to what we did in Step 6. Here we are going to read the OData service meta definition to build the column display.
Our calculation view has six fields CUSTOMERID, EMPLOYEEID, PRODUCTID, ORDERID, NETSALES & COST and we want those fields to be displayed. We also want to implement a filter and sorting functionality as shown below.
Use the following code and activate the “odataAutoTable.view.js”
sap.ui.jsview("views.odataAutoTable", { /** Specifies the Controller belonging to this View. * In the case that it is not implemented, or that "null" is returned, this View does not have a Controller. * @memberOf views.odataAutoTable */ getControllerName : function() { return null; }, /** Is initially called once after the Controller has been instantiated. It is the place where the UI is constructed. * Since the Controller is given to this method, its event handlers can be attached right away. * @memberOf views.odataAutoTable */ createContent : function(oController) { var oLayout = new sap.ui.commons.layout.MatrixLayout({width:"100%"}); var url = "odata/CaSalesOrder.xsodata/"; var oModel = new sap.ui.model.odata.ODataModel(url, false); var oControl; this.oSHTable = new sap.ui.table.Table("soTable",{ visibleRowCount: 10, }); this.oSHTable.setTitle("SALES_ORDERS"); //Table Column Definitions var oMeta = oModel.getServiceMetadata(); var oControl; for ( var i = 0; i < oMeta.dataServices.schema[0].entityType[0].property.length; i++) { var property = oMeta.dataServices.schema[0].entityType[0].property[i]; oControl = new sap.ui.commons.TextField().bindProperty("value",property.name); this.oSHTable.addColumn(new sap.ui.table.Column({label:new sap.ui.commons.Label({text: property.name}), template: oControl, sortProperty: property.name, filterProperty: property.name, filterOperator: sap.ui.model.FilterOperator.EQ, flexible: true, width: "125px" })); } this.oSHTable.setModel(oModel); var sort1 = new sap.ui.model.Sorter("CUSTOMERID", true); this.oSHTable.bindRows({ path: "/salesOrder", sorter: sort1 }); this.oSHTable.setTitle("Sales Orders"); oLayout.createRow(this.oSHTable); return oLayout; } });
Changes are
- Line 26 - 36: to read the Metadata definition and build the column accordingly
- Line 39 - 44: for the filter and sorting functionality.
finally create "table.html" to execute the "odataAutoTable" view in the browser.
<html><head> <meta http-equiv="X-UA-Compatible" content="IE=edge"><script src="/sap/ui5/1/resources/sap-ui-core.js" id="sap-ui-bootstrap" data-sap-ui-libs="sap.ui.ux3,sap.ui.commons,sap.ui.table,sap.viz" data-sap-ui-theme="sap_goldreflection"> </script><script> sap.ui.localResources("views"); var view = sap.ui.view({id:"odataTable", viewName:"views.odataAutoTable", type:sap.ui.core.mvc.ViewType.JS}); view.placeAt("content");</script></head><body class="sapUiBody" role="application"> <div id="content"></div> </body></html>
Conclusion:
As shown here, it's quite easy to build an XS-based application. There are tons of example part of the Table - SAPUI5 Demo Kit you can use for your next big application. Happy Coding.
Happy