Introduction
In this post we will build an application containing an input field with a Value Help dialog. For that, we will implement a Gateway Query Service that returns the requested list of values. Then we will implement a dialog window with a table in it. With a line selection, we will implement an event which closes the dialog and transmits the selected value to the original input field.
Installed components
We used SAP UI5 v1.12, NetWeaver Gateway 2.0 SP6 and the current version of the Chrome Browser.
Gateway Service Implementation
As of NetWeaver Gateway 2.0 SP4 it is recommended to use the Gateway Service Builder to model the Gateway service Entity Data Model (EDM) [1]. The SEGW is the transaction for that. There we create a new project ZMATNR_VALUE_HELP for the value help service.
We will need one entity MaterialCode for our Value Help Service which contains three properties: material number, material type and description.
For that, we create the Entity MaterialCode along with the related EntitySet MaterialCodeSet:
Next, we select the created entity and define the properties Matnr, Mattype and Descr:
We define the Matnr property as the key property.
All properties have defined EDM type, max length, label, ABAP field and ABAP type.
We define the ABAP Type as a Data element, MATNR, MTART, and MAKTX respectively.
The model is finished now, and we can generate the runtime objects – use icon in the menu bar.
We keep the proposed names and generate the ABAP classes.
Check if the generation log is green.
Now we select the data provider class (ending with _DPC_EXT):
When double-click on the name we switch to the class builder. We need to redefine the method MATERIALCODESET_GET_ENTITYSET there.
For simplicity, we put the SELECT statement fetching the data directly from MARA/MAKT tables. In the real life, you would usually call a BAPI or any other business function here fetching the values and mapping them to the service entity (represented by the exporting parameter et_entityset).
METHOD materialcodeset_get_entityset. SELECT m~matnr m~mtart t~maktx INTO CORRESPONDING FIELDS OF TABLE et_entityset FROM ( mara AS m LEFT OUTER JOIN makt AS t ON m~matnr = t~matnr ) UP TO 30 ROWS. ENDMETHOD.
Now we can leave the class and the service builder and go to the Gateway hub (which is in our case the same system). On the Gateway hub we publish the service into the service catalog. For that, we use the transaction /IWFND/MAINT_SERVICE and open up the dialog to add a new service, then provide a system alias for the backend system (for local services we use the alias LOCAL).
We keep all values default and get the service published as follows:
Note: (1) ODATA ICF node must be green, and (2) the System Alias must contain a system alias pointing to the backend system where we created the model and data provider.
Now the service is ready to use and we can test it in the Gateway Client (check the button within the ICF Nodes area).
We change the URL to /sap/opu/odata/sap/ZMATNR_VALUE_HELP_SRV/MaterialCodeSet?$format=json
Then execute the service and check its output.
In our case we’ve got the following JSON document.
In our SAP UI5 application we will use this service for fetching the Material codes and descriptions.
Note, that we have requested the output in the JSON format ($format=json) as this format is directly supported by JavaScript and SAP UI5.
SAP UI5 Application
Our SAP UI5 project’s name in Eclipse is ValueHelp. It contains an initial Javascript view “main”. This is how our project structure looks like:
As we are going to use the UI5 controls ToolPopup and Table, we need to load the relevant UI5 libraries. For that, we need to include them in the so-called bootstrap declaration. We open the index.html and extend the script parameter data-sap-ui-libs with two more libraries sap.ui.table and sap.ui.ux3.
<script src="resources/sap-ui-core.js" id="sap-ui-bootstrap" data-sap-ui-libs="sap.ui.commons,sap.ui.table,sap.ui.ux3" data-sap-ui-theme="sap_goldreflection" > </script>
As the next step we update the main.view.js file and implement the function createContent(). We will implement the input field with the UI5 control sap.ui.commons.ValueHelpField and display a field label with another UI5 control sap.ui.commons.Label.
We also use some layout controls Panel and LayoutMatrix to make the application appearance nicer.
createContent : function(oController) { var oPanel = new sap.ui.commons.Panel({ text : "Select Material Number" }); var oLayoutMatrix = new sap.ui.commons.layout.MatrixLayout({ width : "60%", // control width relative to window widths : [ "30%", "40%", "30%" ] // widths of the columns }); var oMatnrLabel = new sap.ui.commons.Label("idMatnrLabel", {text: "Material Number"}); // Input Field for Material Number with Value Help var oMatnrInput = new sap.ui.commons.ValueHelpField("idMatnrInput", { valueHelpRequest: function(oEvent){ } }); oLayoutMatrix.createRow(oMatnrLabel, oMatnrInput); oPanel.addContent(oLayoutMatrix); return oPanel; }
The output in Chrome looks now as follows.
The control ValueHelpField has an event valueHelpRequest which we implement as a dialog window provided by the UI5 control ToolPopup.
valueHelpRequest: function(oEvent){ var oValueHelpDialog = new sap.ui.ux3.ToolPopup({ modal: true, inverted: false, // disable color inversion title: "Select Material Number", opener: "idMatnrInput", // locate dialog next to this field closed: function (oEvent) { } }); }
Note: the control event closed is not yet implemented; we will do it later to extract the selected value from the table which will be placed in the dialog window.
Now we add a button “OK” to the Dialog window which is used to close the dialog window. This code is placed directly after the declaration of oValueHelpDialog.
var oOkButton = new sap.ui.commons.Button({ text: "OK", press: function (oEvent) { oEvent.getSource().getParent().close(); } }); oValueHelpDialog.addButton(oOkButton); oValueHelpDialog.open();
Now the Value Help dialog window can be opened and closed.
As the next step we place the table with two columns Matnr and Descr into the dialog window. We insert this code after the addButton(oOKButton).
Note: the fields Matnr and Descr must refer to our underlying data model (e.g. provided by the Gateway Service or locally).
var oHelpTable = new sap.ui.table.Table({ selectionMode: sap.ui.table.SelectionMode.Single, visibleRowCount: 7, width: "300pt" }); oHelpTable.addColumn( new sap.ui.table.Column({ label: new sap.ui.commons.Label({text: "Material Number"}), template: new sap.ui.commons.TextView().bindProperty("text", "Matnr"), sortProperty: "Matnr", filterProperty: "Matnr", }) ); oHelpTable.addColumn( new sap.ui.table.Column({ label: new sap.ui.commons.Label({text: "Description"}), template: new sap.ui.commons.TextView().bindProperty("text", "Descr"), sortProperty: "Descr", filterProperty: "Descr", }) ); oValueHelpDialog.addContent(oHelpTable);
Now we can see the dialog window with the empty table:
In the next step we fetch the data either from a Gateway service or define it locally as a JSON array.
We use the global variable DATABINDING to switch between these two modes – we use true for the Gateway service call and false for the local data binding.
Insert this line into your main.controller.js function onInit()
/** * Called when a controller is instantiated and its View controls (if available) are already created. * Can be used to modify the View before it is displayed, to bind event handlers and do other one-time * initialization. */ onInit: function() { DATABINDING = false; },
Now we continue with the main.view.js and insert before the addContent() line the following code:
var oHelpModel = new sap.ui.model.json.JSONModel(); if (DATABINDING) { console.log("Gateway Databinding for Value Help"); // http:///sap/opu/odata/sap//MaterialSet?$select=Matnr,Descr&$format=json var helpURL = "http:///sap/opu/odata/sap//MaterialSet"; var queryString =”$select=Matnr,Descr&$format=json”; oHelpModel.loadData( helpURL, queryString, false); } else { console.log("Local Databinding for Value Help"); oHelpData = {"d":{"results":[ {"Matnr":"4711","Descr":"Snowboard"}, {"Matnr":"4712","Descr":"Mountain Ski"}, {"Matnr":"4713","Descr":"Backcountry Ski"}, {"Matnr":"4714","Descr":"Freeride Ski"}, {"Matnr":"2011001","Descr":"Ski Boots"}, {"Matnr":"2011002","Descr":"Ski Poles"}, {"Matnr":"2011003","Descr":"Rucksack"}, {"Matnr":"5550001","Descr":"Ski Googles"}, {"Matnr":"5550002","Descr":"Ski Helmet"}, {"Matnr":"5550007","Descr":"GPS Unit"} ]}}; oHelpModel.setData(oHelpData); } oHelpTable.setModel(oHelpModel); oHelpTable.bindAggregation("rows", "/d/results");
The structure of the local JSON object is aligned with the Odata Entity Model Structure, so that the same data binding can be used here for local and Gateway data binding.
Now the dialog window shows up our test data:
However, the selection is not yet transferred back to the input field.
For that, we implement the event closed of the ToolPopup control which we already defined but left empty.
closed: function (oEvent) { // return selected tabled line/value var oCore = sap.ui.getCore(); var oMatnrInput = oCore.byId("idMatnrInput"); var oContext = oHelpTable.getContextByIndex(oHelpTable.getSelectedIndex()); if (oContext) { var oSel = oContext.getModel().getProperty(oContext.getPath()); oMatnrInput.setValue(oSel["Matnr"]); }; }
The value of the selected row can be showed now in the input field:
Postscriptum
All code samples can be tested within an Eclipse project. Run it in Eclipse as a Web application (we used the Jetty http server plugin for that) and copy the URL into the Chrome Browser. Keep the Jetty window open, and refresh your browser window (F5) to re-run the application. To see the console output, use F12 in the browser to switch to the debugging mode.
References
[1] Entity Data Model (EDM) – http://www.odata.org/documentation/overview#41_Entity_Data_Model_EDM_Overview
[2] UI5 Blog – Javascript Crash Course for ABAP Developers http://scn.sap.com/blogs/ui5-for-abap/2013/05/02/javascript-crash-course-for-abap-developers
About the authors
Vladislav Bezrukov is Development Architect at SAP Consulting focusing on integration technologies, Web and REST Services in ABAP NetWeaver platform (ABAP WS, Gateway). His further professional interests extend to SAP UI5 and HANA technologies, which he is using in his projects in the past couple of years. Email: vladislav.bezrukov@sap.com
Florian Backfisch is Development Consultant at SAP Consulting. His professional interests are ABAP and UI technologies. Email: florian.backfisch@sap.com
Johannes Demund is Development Consultant at SAP Consulting. He focuses on ABAP technologies and user interfaces, including NetWeaver Business Client, WebDynpro ABAP, and SAP UI5. Email: johannes.demund@sap.com
Disclaimer
This blog does not represent nor substitute the official SAP documentation. The views expressed by the authors do not necessarily reflect the views of their employer.
Really nice idea. Will definitely test it as soon as I have the time.
Accidently came across this. I didnt even know we had a valuehelp field in commons library. This will help us a lot. Thanks.
Hi Vladislav,
I implemented this Valuehelp in one of our UI5 application. The Valuehelpfield is towards the right of the screen. When the value help opens, it is now opening outside the screen and a horizontal scroll bar appears which looks pretty awkward. Did you run into any situation like this? Also, I couldnt give any setting in the ToolPopup api or ValueHelpfield api so that the popup opens below the control instead of right side of the control. If you have any inputs, that would be great.
Sathish.
Hi Sathish,
for the problem with window placement, you can control it by the property “opener” on the ToolPopup control; see control help for further details.
Check the UI5 version; I am aware of some problems with the versions 1.10 and below. 1.12 and higher should be fine.
Hi Vladislav,
Thanks, I implemented with the opener. I found some help from UI5 SDK. I have another question. I tried to implement the above example. In my F4 help, I have to add some search fields and based on the search critieria, I want to retrieve the results.
I used the following code:
var material=sap.ui.getCore().getControl(“material”).getValue();
var desc = sap.ui.getCore().getControl(“desc”).getValue();
var url = ‘/MATERIALSet?$filter=substringof(‘+”‘”+material+”‘”+’,ExtProdID) and substringof(‘+”‘”+desc+”‘”+’,ProductDesc)’;
var oModel = sap.ui.getCore().getModel();
oModel.read(url, null, null, true, function(oData, oResponse){
sap.ui.getCore().byId(“HT1”).setModel();
var sort1 = new sap.ui.model.Sorter(“ExtProdID”);
sap.ui.getCore().byId(“HT1”).bindRows(url,null);
}, function(){
alert(“Data not found”);});
I get two problems in the above code:
1. bindRows(url, null) returns an error. My oData service is fired correctly and the values are retrieved but at the time it is binding, somehow $count is added at the end of the actual url. Since there are filter parameters already, this syntax is not correct. So I get error in the console log.
2. ABAP Gateway is called two times. Once during oModel.read and the second during bindRows.
I tried /../sap/opu/odata/sap/ZPRODUCTS_SRV/MATERIALSet?$filter=substringof(‘A1’,ExtProdID)$select=ExtProdID&$format=json
I got this error message: In the context of Data Services an unknown internal server error occured.
If I dont use the filter, it is working fine.
Do you have any suggestion on how to do filter parameters?
Great Stuff.
Hi Vladislav ,
thank you so much for this great tutorial!!
Currently, I want to implement multiple value help dialogs.
What do you recommend? Using only one definition of the control/method or using multiple controls /methods for each value input field?
Is there a good possibility to achieve that dynamically?
Best regards,
Rufat
Thanks !
Very useful..
thanks for sharing.
Praveer.
I will surely try it out. Nice stuff.
Thanks for sharing
Is there a way to add an invisible column or set a Key for the Value so instead of getValue i could get getSelectedKey or smth. Because i have a model which consists of a ValueId and ValueText. I want to display the ValueText, but when I want to write to database i need to get the ValueId which corresponds to the ValueText. How can I do that without creating a separate column for it?
Great Post,
Thanks for sharing !!
Best Regards,
PavanG
Its very useful thank u..:)
Nice Post,
But when implementing iti am getting an error that the databinding is not defined can someone please help me with this. I am new to sapui5.
Thanks and regards,
Rohan