Skip to Content

Usability is the Key to a successful application. I believe user interaction should be seamless, and Fiori guidelines help us accomplish this goal.   I wanted to share my experience on developing some basic functionality in a simple Fiori App.  I will use Worklist app (list records on first Master page, clicking on the record takes to the Object page that allows editing) as a template. Worklist Apps work great, but I felt it’s a lot of navigation if you are updating multiple records.

There are many useful code samples on the net showing similar functionality, so hopefully you can benefit little bit from each postings to develop your ideal project.

In this project, the following functionality will be developed:

  1. Display data from a Z table.
  2. Edit any fields on the screen, and click the “SAVE ALL” button that updates all the changed records in the backend.
  3. “ADD” button pops up a Dialog box allows you to add new record.
  4. “DELETE” button pops up a confirmation message, and upon confirmation, record is deleted.

Screenshot of the finished application:

 

oData Service

I will not go into depth of oData service creation for this section, as this has been described in many other really good tutorials.

One thing to note that your oData service must be prepared to handle batch requests.

The way to accomplish this is:

  • Go to to transaction SEGW.
  • Select your Web Service project > expand Runtime Artifacts. Right click on *_EX_DPC_EXT and select Go to ABAP Workbench.

Select the following methods, and press the button Redefine.

  • Just place the code EXIT
  • Activate everything.

Do this for both of the following methods:

  • /IWBEP/IF_MGW_CORE_SRV_RUNTIME~CHANGESET_BEGIN
  • /IWBEP/IF_MGW_CORE_SRV_RUNTIME~CHANGESET_END

 

Here is a link that describes this process is detail:

https://help.sap.com/viewer/0ce0b8c56fa74dd897fffda8407e8272/7.5.6/en-US/6c47b2b39db9404582994070ec3d57a2.html

Developing the App

In SAP Web IDE, Create new Project using Worklist template, and your oData service that fetches the data from a Z table.

Make all necessary fields editable:

 

Add onChange Event (call function: onFieldValueChange) to all Editable Fields:

 

This code adds the Record ID to a JavaScript array (gChangedInspsArr). We will later use this array to determine which records have been changed.  With oData, You can update entire table every time, but here we are trying to only filter out records that are updated.

/**
 *@ Value changed for a field. add Inspector ID to array, so we can update all together in SaveAll method.
*/
onFieldValueChange: function(oControlEvent) {

//This code was generated by the layout editor.
var oSelectedItem = oControlEvent.getSource();
var obj = oControlEvent.getSource().getBindingContext().getObject();
		
//Add Change Inspector to Array		
gChangedInspsArr.push(obj.InspId);
},

 

SAVE ALL Button

Pseudo code for SAVE ALL method.

  • Loop trough changed records.
  • Use Group ID updateRecsGrpId to group all changes. These changes will not be submitted until submitChange() method is called with groupid = updateRecsGrpId parameter.
	/**
		 * Handle Save All Button Event
		 * Check which records have been changed, and update the Model in batch
		 * @public
		 */
		onSaveAll: function() {
			var oTable = this.byId("Table");
			var oModel = this.getOwnerComponent().getModel();
			var oTable = that.getView().byId("Table");
			var a = 0;

			//Loop through entire table, and add changed records in 
			oTable.getItems().forEach(function(row) {
				var oEntry = {};
				var obj = row.getBindingContext().getObject();
				if (gChangedInspsArr.indexOf(obj.InspId) > -1) { //If record was changed
						var cells = row.getCells();
						oEntry.Id = cells[0].getTitle();
						oEntry.FieldOne = cells[1].getSelected();
						oEntry.FieldTwo = cells[2].getValue();
						oEntry.FieldThree = cells[3].getValue();
					oModel.update("/ZTABLEENTITY_Set('" + oEntry.Id + "')", oEntry, {
						groupId: "updateRecsGrpId",
						changeSetId: "InspChangeID"
					});
				}
			});

			oModel.submitChanges({
				groupId: "updateRecsGrpId",

				success: function(oData, oResponse) {
					gChangedInspsArr = [];
					sap.m.MessageToast.show("Records Updated");
				},
				error: function(oError) {
					sap.m.MessageToast.show("Record Not Updated. Please check with Admin.");
				}
			});
		},

 

 

 

ADD Button:

Records are added using a popup Dialog Box; (I.e we can easily add a row to the table, but I am using a Dialog box just to show the different user interaction options available).

 

Event behind the +ADD button:

		/**
		 * Event handler for getting 'Add' Dialog started
		 * @public
		 */
		handleOpenDialog: function(evt) {
			this.getDialogAdd().open(); // get the reference of input fields of fragment and set the values  
		},

That calls:

		/**
		 * Event handler for getting 'Add' Dialog started
		 * @public
		 */
		getDialogAdd: function() {
			if (!this.dialog) {
				// This fragment can be instantiated from a controller as follows:  
				this.dialog = sap.ui.xmlfragment("fragPopupDialog", "Z_UPDATE_TABLE.view.DialogAdd", this);
			}
			return this.dialog;
		},

Code for the XML Fragment:

<core:FragmentDefinition xmlns="sap.m" xmlns:l="sap.ui.layout" xmlns:f="sap.ui.layout.form" xmlns:core="sap.ui.core">  
    
    <Dialog title="Add Data">  
    <l:Grid defaultSpan="L12 M12 S12" width="auto" id="idGrid">  
    <l:content>  
        <f:SimpleForm id="formAdd" minWidth="1024" maxContainerCols="2" editable="false" layout="ResponsiveGridLayout" title="Data" labelSpanL="3" labelSpanM="3" emptySpanL="4" emptySpanM="4" columnsL="2" columnsM="2">  
            <f:content>  
                <Label text="ID"/>  
                <Input id="i_INSP_ID" type="Number" maxLength="8" editable="true" required="true" showValueHelp="true" valueHelpRequest="handleInspValueHelp"/>  
                
                <CheckBox id="i_ COUNT_EXCLUDE" name="WRM_COUNT_EXCLUDE" text="Exclude "/> 

                <Label text="Field One"/>  
                <Input id=" i_ FIELD_2" type="Number" maxLength="10"/>  

                <Label text="Field Two"/>  
                <Input id=" i_ FIELD_2" type="Number" maxLength="10"/> 

                <Label text="Field Three"/>  
                <Input id=" i_ FIELD_3" type="Number" maxLength="10"/> 

                <Label text="Field Four"/>  
                <Input id=" i_ FIELD_4" type="Number" maxLength="10"/> 

                <Label text="Field Five"/>  
                <Input id=" i_ FIELD_5" type="Number" maxLength="10"/> 

                <Label text="Description"/>  
                <Input id="i_DESCRIPTION" maxLength="254"/>                 
                
            </f:content>  
        </f:SimpleForm>  
    </l:content>  
    </l:Grid>  
    <buttons>  
    <Button text="CLOSE" press="closeDialog" type="Reject"/>  
    <Button text="SAVE" press="onAdd" type="Accept"/>  
    </buttons>  
    </Dialog>  
</core:FragmentDefinition>

The ‘Save’ button (green) takes all input values from popup box, and then call then call the model:

		/**
		 * Handle Save event for New Inspector
		 * @public
		 */
		onAdd: function() {
			//Start prepairing input Entry
			var oEntry = {};
			var content = sap.ui.getCore().byId("fragPopupDialog--formAdd").getContent();
			oEntry.InspId = sap.ui.getCore().byId("fragPopupDialog--i_INSP_ID").getValue();
			//TODO: get values of each field here…

			//Get Model
			var oModel = this.getOwnerComponent().getModel();
			oModel.create("/ZCFG_ENTITYSet", oEntry, {
				success: function(oData, oResponse) {
					sap.m.MessageToast.show("Record Created");
					oModel.updateBindings();
				},
				error: function(oError) {
					sap.m.MessageToast.show(sap.ui.getCore().getMessageManager().getMessageModel().oData[0].message);
					oModel.refresh();
				}
			});
			this.closeDialog();
		},

Screenshot: Popup Add dialog box:

 

Image: Record Created upon Saving:

 

DELETE Button

 

*Note: In order to set the Table in Delete mode, you have to set the table control with following attributes.

 

<Table id="inspTable" width="auto" items="{ path: '/ZCFG_ENTITYSet', sorter: { path: 'InspId', descending: false } }" delete="onDeletePress" mode="Delete">

 

Screenshot: Clicking on Red X icon for specific record for deletion:

 

Screenshot: Record Deleted upon confirmation:

Handler for the Delete Button Event:

		/**
		 * Handle Inspector Delete Event
		 * Popupp Confirmation
		 * @public
		 */
		onDeletePress: function(oEvent) {
			//This code was generated by the layout editor.
			var oSelectedItem = oEvent.getParameter("listItem");
			var sItemName = oSelectedItem.getBindingContext().getProperty("InspId");

			gDeleteInspId = sItemName;
			sap.ui.commons.MessageBox.confirm("Remove Record for Inspector:" + sItemName + "'?", this.doDelete);
		},

This method actually performs the Deletion: 

		 * Perform Deletion of Record
		 * @public
		 */
		doDelete: function(sResult) {

			if (sResult === true) {
				var oModel = that.getOwnerComponent().getModel();
				oModel.remove("/ZCFG_ENTITYSet('" + gDeleteInspId + "')", {
					success: function(oData, oResponse) {
						gDeleteInspId = '';
						sap.m.MessageToast.show("Record Deleted...");
						oModel.updateBindings();
					},
					error: function(oError) {
						gDeleteInspId = '';
						sap.m.MessageToast.show("Record Not Deleted. Please check with Admin.");
						oModel.refresh();
					}
				});
			}
		},

 

I hope these pieces of code snippets benefit your project.   And, as always, feel free to correct me at any point, as I believe in the fail-fast principle.

Best of Luck, and Happy developing 🙂

Harman Shahi

 

To report this post you need to login first.

Be the first to leave a comment

You must be Logged on to comment or reply to a post.

Leave a Reply