When using SAP Gateway services with operations mapped to remote-enabled function modules (as opposed to the recommended code-based implementation), messages can be returned and presented with a variety of UI5 controls.
To demonstrate message handling, I created a Fiori compliant app within the Gateway Central Hub deployment scenario,
The app displays messages in a Message Popover or Message Box when an entity is updated.
In the back-end SAP Business Suite system, a custom table and a corresponding table type have two fields,
Remote-enabled function modules perform create, read, update, delete, and query operations on the table as indicated in the following table:
Function Module | IMPORTING TYPE | EXPORTING TYPE | ABAP Keyword |
---|---|---|---|
Query | Table Type | SELECT | |
Read | Table | Table | SELECT SINGLE |
Update | Table | MODIFY | |
Delete | Table | DELETE | |
Create | Table | INSERT |
Additionally, the update function module exports parameter ET_RETURN of type BAPIRET2_TAB to return a table of messages as described in Mapping Delete and Update Operations,
For demo purposes, I simply hard-coded six messages using BAPIRET2's TYPE and MESSAGE fields, two messages per type: information, Warning, and Error.
is_return-type = 'I'.
is_return-message = 'Info message 1'.
APPEND is_return TO et_return.
is_return-type = 'I'.
is_return-message = 'Info message 2'.
APPEND is_return TO et_return.
is_return-type = 'W'.
is_return-message = 'Warning message 1'.
APPEND is_return TO et_return.
is_return-type = 'W'.
is_return-message = 'Warning message 2'.
APPEND is_return TO et_return.
is_return-type = 'E'.
is_return-message = 'Error message 1'.
APPEND is_return TO et_return.
is_return-type = 'E'.
is_return-message = 'Error message 2'.
APPEND is_return TO et_return.
The Gateway framework formulates traditional, verbose SAP messages if fields such as MESSAGE_V1 are used.
In the front-end system, a Gateway Service Builder project was created in transaction SEGW.
The data model entity 'Thing' was created by importing RFC/BOR interface parameters. Similarly, operations are mapped to the corresponding function module parameters.
In the entity 'Thing', property 'Description' is updatable.
Finally, we arrive at more glamorous activities involving SAPUI5, Fiori, and the ubiquitous The Cloud.
Web IDE provides templates for easily generating SAPUI5 apps from a Gateway service. Web IDE can be installed on-premises or on SAP HANA Cloud Platform using the SAP HANA Cloud Connector to access on-premises Gateway systems.
To quickly create the app, I used the Fiori Master Detail template with the Gateway service entity set 'ThingsSet' and replaced the Detail view content with a sap.ui.comp.smartform.SmartForm.
<mvc:View
controllerName="com.pmc.messages.view.Detail"
xmlns="sap.m"
xmlns:core="sap.ui.core"
xmlns:mvc="sap.ui.core.mvc"
xmlns:l="sap.ui.layout"
xmlns:f="sap.ui.layout.form"
xmlns:smartField="sap.ui.comp.smartfield"
xmlns:smartForm="sap.ui.comp.smartform">
<Page
id="detailPage"
navButtonPress="onNavBack"
title="{i18n>detailTitle}"
showNavButton="{device>/isPhone}">
<content>
<smartForm:SmartForm
id="smartForm"
checkButton="false"
editTogglable="true"
editToggled="editToggled">
<smartForm:Group label="Thing">
<smartForm:GroupElement>
<smartField:SmartField value="{ThingID}" />
</smartForm:GroupElement>
<smartForm:GroupElement>
<smartField:SmartField value="{Description}" />
</smartForm:GroupElement>
</smartForm:Group>
</smartForm:SmartForm>
</content>
<footer
id="detailFooter">
<Toolbar id="detailToolbar">
<content>
<ToolbarSpacer/>
<Button
id="buttonMessages"
icon="sap-icon://alert"
press="messagePopoverOpen"
text="{controls>/buttonMessagesText}"
type="{controls>/buttonMessagesType}">
</Button>
<Button
id="buttonUpdate"
enabled="{controls>/buttonUpdateEnabled}"
press="thingUpdate"
icon="sap-icon://save">
</Button>
</content>
</Toolbar>
</footer>
</Page>
</mvc:View>
As described in the SmartForm samples, the SmartForm control "creates a form for an OData entity set based on the OData metadata". Since property 'Description' is updatable in the OData metadata, the description is updatable in change mode.
I use a local JSON model to handle control properties such as type and enabled. In the Detail view's controller, the SmartForm's editToggled function enables and disables a Save button by setting the model property to which the Save button's enabled property is bound.
editToggled: function(oEvent){
var oModel = this.getView().getModel("controls"),
bEnabled = oEvent.getParameter("editable");
if(bEnabled === true){
oModel.setProperty("/buttonUpdateEnabled", true);
} else {
oModel.setProperty("/buttonUpdateEnabled", false);
}
},
If a SmartForm's property bindings are changed such as editing the Description property, the changes can be submitted and an Update operation triggered using the OData model's submitChanges method.
The Save button's press function:
The error callback:
Additionally, a Message Box is created for demo purposes.
Below are the Save button's press function thingUpdate() as well as the Message Popover.
thingUpdate: function(){
var oView = this.getView(),
oModel = oView.getModel(),
oModelControls = oView.getModel("controls"),
oModelMessages = oView.getModel("messages");
oModel.submitChanges(
function(){
// Success
},
function(oError){
var oBody = JSON.parse(oError.response.body),
errorDetails = oBody.error.innererror.errordetails,
aErrorDetails = [],
sMessage = "";
if(errorDetails){
aErrorDetails = errorDetails.reduce(function(a,b){
function indexOfProperty (a, b){
for (var i=0;i<a.length;i++){
if(a[i].message == b.message){
return i;
}
}
return -1;
}
if (indexOfProperty(a,b) < 0 ) a.push(b);
return a;
},[]);
for(i = 0, len = aErrorDetails.length; i < len; i++){
var message = aErrorDetails[i].message + "\n\n";
sMessage += message;
}
jQuery.sap.require("sap.m.MessageBox");
sap.m.MessageBox.show( sMessage, sap.m.MessageBox.Icon.NONE, "Update Messages" );
oModelControls.setProperty("/buttonMessagesText",aErrorDetails.length);
oModelControls.setProperty("/buttonMessagesType", "Emphasized");
oModelMessages.setProperty("/messageSet",aErrorDetails);
}
}
);
},
The reduce() function eliminates duplicate array objects (messages) as suggested at Stack Overflow.
<core:FragmentDefinition
xmlns="sap.m"
xmlns:core="sap.ui.core">
<MessagePopover
items="{/messageSet}">
<MessagePopoverItem
type="{
path: 'severity',
formatter: 'com.pmc.messages.util.Formatter.formatMessageType'
}"
title="{
path: 'severity',
formatter: 'com.pmc.messages.util.Formatter.formatMessageTitle'
}"
description="{message}">
</MessagePopoverItem>
</MessagePopover>
</core:FragmentDefinition>
Overall, I tried to let the tools do the hard work,
--- Scott Stefanich