How to perform mass upload from an Excel file in a FIORI App in SAP BTP ABAP Environment – Part 2

Estimated read time 26 min read

Introduction

In continuation to my previous blog post (How to perform mass upload from an Excel file in a FIORI App in SAP BTP ABAP Environment – Part 1), in this blog post, I will showcase an easy way to extend a FIORI Elements-based OData V4 application in Business Application Studio (BAS) to add a custom action on the responsive table header of the list report page. This custom action will allow the user to open a dialog to browse local files and upload an Excel file. 

In this blog post, I will be creating a Fiori Elements-based application using the tools provided in SAP Business Application Studio (BAS) and will be extending generated apps using App Extensions to trigger OData V4 operations (in this example, RAP Static Action ‘fileUpload‘ to upload an Excel file and RAP Static Function ‘downloadFile‘ to download an Excel template).

Prerequisites

SAP Business Application Studio (BAS) subscription and developer role in your respective BTP Subaccount.Basic knowledge of performing App Extensions to extend generated FIORI Elements based apps.Basic UI5 skill.Use of File Uploader control.

Basic of OData V4 Operation

Function – Functions are operations exposed by an OData service that don’t have side effects. Functions must return data and can include additional path segments. Functions are invoked using HTTP method GET.

Action – Actions are operation exposed by an OData service that can have side effects. Actions can return data but must not be composed with additional path segments. Actions are invoked using HTTP method POST.

Operation – Both Functions and Actions are operations that can return data. Operations are either bound to a resource (for example, an entity type), that makes them members of that instance type. Operations can also be unbound. Unbound operations are called as static operations (using “action imports” or “function imports”) since a static (unbound) operation can’t be called directly.

The OData V4 model supports OData operations (Action ImportFunction Import, Bound Actions and Bound Functions).

RAP – Non-Standard Operations

Non-standard operations are RAP BO Operations that provide transactional behavior and that are user defined and implemented. There are two kinds of non-standard operations.

Action: User-implemented operations that change the data of a BO instance. They are self-implemented operations. Two main categories of actions can be implemented in RAP:

Non-factory actions: Defines a RAP action which offers non-standard behavior. The custom logic must be implemented in the RAP handler method FOR MODIFY. An action per default relates to a RAP BO entity instance and changes the state of the instance. An action is related to an instance by default. If the optional keyword static is used, the action is defined as static action. Static actions are not bound to any instance of a RAP BO entity but relate to the complete entity.

Factory actions: Factory actions are used to create RAP BO entity instances. Factory actions can be instance-bound (default) or static. Instance-bound factory actions can copy specific values of an instance. Static factory actions can be used to create instances with prefilled default values.

Function: User-implemented operations that return data without any side effects.Instance Function: Bound to an instance of a RAP BO Entity which can have input parameters.Static Function: Not bound to any instance of a RAP BO Entity but relate to the complete entity. It can also have input parameters.

Can we create unbound OData V4 operations “Action Import” or “Function Import” in RAP?

At this moment, we cannot implement unbound function and action in RAP. Both the static action and functions are related to the complete entity. But it is supported in CAP.

Code sample:

How RAP Static Action and Functions are related to the complete entity?

Let’s understand from the metadata itself.

It is bound to the complete entity and with the collection of the entity. 

Let’s begin

For the interest of time and topic, I am not showing all the basic steps to create a FIORI Elements based application in BAS. There are multiple blog posts and SAP Learning materials available to refer. I will directly jump to perform app extension.

We will be using the Fiori Guided Development tool to add a custom action to a page using extension and will be using the page map to perform List Report page controller extension.

Note: When you use the Fiori Guided Development tool to add a custom action in OData V4 based app, the framework automatically adds a custom action handler function in JavaScript creating a new custom JS file but does not perform controller extension. This is the way OData V4 app extension behaves which adds a “controlConfiguration” in the target page.

Custom Action in the Table toolbar for the list report in OData V2 vs OData V4:

OData V2:

OData V4:

Further Reference: Adding Custom Actions Using Extension Points 
But why do we need a controller extension? Can’t we use the custom action handler function created automatically by the Guided Development tool in an OData V4 app?

We need to create a controller extension in an OData V4 app explicitly to use the SAP FIORI Elements extension API to open a dialog using the ‘loadFragement’ function; to perform OData operations and to use Edit Flow (A controller extension offering hooks into the edit flow of the application) to invoke action and perform secured execution. Otherwise, within the custom JS file, we won’t get the model reference to trigger the OData operation (here RAP Static Action and Static Function).

So, we will start by adding a custom action to the table toolbar using the FIORI Guided Development tool.

Step 1: Right click on the ‘webapp’ folder and select ‘Open Guided Development’

Step 2: Select ‘Add a custom action to a page using extensions’.

Step 3: Follow the guide and select the page and choose a custom controller and function name.

Following code snippets to be inserted.

Go ahead and insert snippets. This will modify the manifest settings and create custom JS file.

Step 4: But we need to perform controller extension. So, the custom JS file can be deleted. 

Step 5: Now, we will perform the List report controller extension using the page map. So, right click on ‘webapp’ folder and select ‘Show Page Map’.

Step 6: From the page map, select the List Report page and click on ‘Show controller extension’. After that, click on ‘Add Controller Extension’ to create controller extension on the list report page.

Note: It is advisable to perform controller extensions for the concerned page rather than performing a generic extension for all the list report pages. Here, ‘VendorEmailList’ is the target list report page. So, I selected that option.

Step 7: Now, we can navigate to the controller extension using ‘Edit in source code’ button and add custom logic.

This the way the controller extension looks.

Let us add a custom handler function ‘uploadMailDialog’ to handle the custom action.

After the controller extension, the following changes applied to the manifest.json file. It signifies a controller extension added to the list report controller.

Step 8: Now, we need to tweak the manifest settings in such a way that the custom action triggers the custom handler function ‘uploadMailDialog’ added to the controller extension.

We need to add the custom action path adding ‘.extension as a prefix. ‘.extension’ refers to the controller extension.

Step 9: Now, if we preview the application and click on the custom action button, it will trigger the controller extension.

Step 10: Let us now add a fragment to open the file dialog. So, we will use ‘fileUploader’ control. I created a fragment folder inside the ‘ext’ folder and added a new xml file ‘uploadFileDialog.fragement.xml’.

Following is the sample code snippet.

 

 

<core:FragmentDefinition xmlns:core=”sap.ui.core” xmlns:f=”sap.ui.layout.form” xmlns:u=”sap.ui.unified” xmlns=”sap.m”
xmlns:layout=”sap.ui.layout”>
<Dialog id=”idFileDialog” title=”{i18n>uploadVendorMailDialogTitle}” >

<VBox id=”idVBox” width=”100%”>
<core:InvisibleText id=”idInvisibleText” text=”{i18n>uploadVendorMailDialogTitle}”/>
<f:SimpleForm id=”idSimpleForm” editable=”true” layout=”ResponsiveGridLayout” maxContainerCols=”2″>
<f:content>
<Label id=”idFileUploadlabel” required=”true” text=”{i18n>uploadVendorMailFile}”/>
<u:FileUploader id=”idFileUpload” name=”internalMailFileUpload” change=”onFileChange” width=”100%”
uploadComplete=”onUploadComplete” style=”Emphasized” fileType=”xls,xlsx” placeholder=”{i18n>uploadVendorMailPlaceholder}”
tooltip=”{i18n>uploadVendorMailTooolTip}” sendXHR=”false” />
</f:content>
</f:SimpleForm>
</VBox>
<footer>
<Toolbar id=”idFooterToolbar”>
<content>
<Button id=”idDownloadTempButton” text=”{i18n>downloadTempButtonTxt}” press=”onTempDownload” icon= “sap-icon://download-from-cloud” />
<Button id=”idUploadButton” text=”{i18n>uploadButtonTxt}” type=”Emphasized” press=”onUploadPress” icon=”sap-icon://upload-to-cloud” />
<Button id=”idCancelButton” text=”{i18n>cancelButtonTxt}” press=”onCancelPress” icon=”sap-icon://cancel”/>
</content>
</Toolbar>
</footer>
</Dialog>
</core:FragmentDefinition>

 

 

Now, add the logic to open the dialog on click of the action similar to the code snippet mentioned below.

 

 

uploadMailDialog: function (oEvent) {
this.base.getExtensionAPI().loadFragment({
name: Constants.fragmentName,
type: “XML”,
controller: this
}).then(function (oDialogResult) {
oDialog = oDialogResult;
oDialogResult.open();
})
},

 

 

Now, in application preview the app looks like below.

Step 11: So, till now, on click of the action ‘Upload’, a dialog will open up asking the user to upload a file. The button ‘Template’ will allow users to download an Excel template, and the button ‘Upload’ will allow users to upload the given file.

Let us add logic to read the file content, mimetype and filename of the given file. I used ‘onFileChange‘ event of the File uploader control as highlighted below.

 

 

// On File Change
onFileChange: function (oEvent) {
// Read file
var file = oEvent.getParameter(“files”)[0];
if (file === undefined) {
return;
}
fileType = file.type; //mimetype or file type
fileName = file.name;
//Instantiate JavaScript FileReader API
var fileReader = new FileReader();
//Read file content using JavaScript FileReader API
var readFile = function onReadFile(file) {
return new Promise(function (resolve) {
fileReader.onload = function (loadEvent) {
resolve(loadEvent.target.result.match(/,(.*)$/)[1]);
};
fileReader.readAsDataURL(file);
});
};

new Action(readFile(file)).executeWithBusyIndicator().then(function (result) {
fileContent = result;
})
},

 

 

Here, I used JS FileReader API to read the file content as data URL and later used RegEx to clean the base64 encoded file content. 

Step 12: Now, implement the logic to upload a file once the ‘upload’ button is clicked.

 

 

//perform upload
onUploadPress: function (oEvent) {
var oResourceBundle = this.base.getView().getModel(“i18n”).getResourceBundle();
//check file has been entered
if (fileContent === undefined || fileContent === “”) {
MessageToast.show(oResourceBundle.getText(“uploadFileErrMsg”));
return;
}

var oModel = this.base.getExtensionAPI().getModel();

var oOperation = oModel.bindContext(“/VendorEmail” + Constants.serviceNamespace + “fileUpload(…)”);

var fnSuccess = function () {
oModel.refresh();
MessageToast.show(oResourceBundle.getText(“uploadFileSuccMsg”));
oDialog.close();
//Clear the file name from file uploader
sap.ui.getCore().byId(“idFileUpload”).clear();
oDialog.destroy();
fileContent = undefined;
}.bind(this);

var fnError = function (oError) {
this.base.editFlow.securedExecution(
function () {
Messaging.addMessages(
new sap.ui.core.message.Message({
message: oError.message,
target: “”,
persistent: true,
type: sap.ui.core.MessageType.Error,
code: oError.error.code
})
);
var aErrorDetail = oError.error.details;
aErrorDetail.forEach((error) => {
Messaging.addMessages(
new sap.ui.core.message.Message({
message: error.message,
target: “”,
persistent: true,
type: sap.ui.core.MessageType.Error,
code: error.code
})
);
})
}
);
oDialog.close();
//Clear the file name from file uploader
sap.ui.getCore().byId(“idFileUpload”).clear();
oDialog.destroy();
fileContent = undefined;
}.bind(this);

oOperation.setParameter(“mimeType”, fileType);
oOperation.setParameter(“fileName”, fileName);
oOperation.setParameter(“fileContent”, fileContent);
oOperation.setParameter(“process”, sProcess);
oOperation.execute().then(fnSuccess, fnError);
},

 

 

Here, I triggered OData V4 operation a bound action (RAP ‘fileUpload’ Static Action) passing File Type, File Name and File Content as parameters.

Earlier, I already explained that a RAP Static Action or Function is bound to a collection specified by an OData entity set. So, to call such actions or functions, you can create a context binding with an absolute path, or with a relative path for the operation (for example odata.srv.name.space.staticMyAction(…)”) and the header context of a list binding as parent context. The following sample shows a button press event handler which calls the static MyAction action on the MyEntity entity set.

 

var oModel = this.getView().getModel();

oModel.bindContext(“/MyEntity/odata.srv.name.space.staticMyAction(…)”).invoke();

 

The same example with a relative binding and the header context of the list binding as parent context:

 

var oModel = this.getView().getModel(),
// assume there is a table with ID “idMyEntityTab” and its items aggregation bound to “/MyEntity”
oListBinding = this.byId(“idMyEntityTab”).getBinding(“items”),
oHeaderContext = oListBinding.getHeaderContext();

oModel.bindContext(“name.space.staticMyAction(…)”, oHeaderContext).invoke();

 

Note: In my code sample, I am using “execute” function of the OData V4 Context binding. But, from UI5 version 1.123.0 onwards, “execute” function is deprecated. So, use “invoke” function instead as shown in the example. Simply, replace “execute’ function with “invoke”.

 

oOperation.setParameter(“mimeType”, fileType);
oOperation.setParameter(“fileName”, fileName);
oOperation.setParameter(“fileContent”, fileContent);
oOperation.setParameter(“process”, sProcess);
oOperation.invoke().then(fnSuccess, fnError);

 

Reference: Execute function of OData V4 Context Binding API 

Alternatively, you can also use the “invokeAction” method of the Edit Flow API ( Edit Flow API ). However, I found the above approach to be the preferred choice.

Reference: How to call RAP static action from elements FPM extension with EditFlow.invokeAction()? 

After this step when you will execute the ‘upload’ action selecting an Excel file to upload, the RAP static action ‘fileUpload’ will trigger and the XCO XLSX module will do the rest to parse the file content and create/update BO entity instances as explained in my previous blog post.

Step 13: Similar to the file upload, the excel template can also be downloaded invoking RAP static function ‘downloadFile’.

Sample code snippet:

 

onTempDownload: function (oEvent) {
var oModel = this.base.getExtensionAPI().getModel(),
oResourceBundle = this.base.getView().getModel(“i18n”).getResourceBundle();

var oModel = this.base.getExtensionAPI().getModel(),
oResourceBundle = this.base.getView().getModel(“i18n”).getResourceBundle();

var oOperation = oModel.bindContext(“/VendorEmail” + Constants.serviceNamespace + “downloadFile(…)”);

//Success function to display success messages from OData Operation
var fnSuccess = function () {
var oResults = oOperation.getBoundContext().getObject();

var aUint8Array = Uint8Array.from(atob(oResults.fileContent), c => c.charCodeAt(0)),
oblob = new Blob([aUint8Array], { type: oResults.mimeType });

File.save(oblob, oResults.fileName, oResults.fileExtension, oResults.mimeType);
MessageToast.show(oResourceBundle.getText(“downloadTempSuccMsg”));
}.bind(this);

//Error function to display error messages from OData Operation
var fnError = function () {
this.base.editFlow.securedExecution(
function () {
Messaging.addMessages(
new sap.ui.core.message.Message({
message: oError.message,
target: “”,
persistent: true,
type: sap.ui.core.MessageType.Error,
code: oError.error.code
})
);
var aErrorDetail = oError.error.details;
aErrorDetail.forEach((error) => {
Messaging.addMessages(
new sap.ui.core.message.Message({
message: error.message,
target: “”,
persistent: true,
type: sap.ui.core.MessageType.Error,
code: error.code
})
);
})
}
);
}.bind(this);

// Execute OData V4 operation i.e a static function ‘downloadFile’ to download the excel template
oOperation.execute().then(fnSuccess, fnError)
// From UI5 version 1.123.0 onwards use invoke function
//oOperation.invoke().then(fnSuccess, fnError);

 

Please stay tuned for the next blog post where I will explain how I used the XCO XLSX module to create an Excel template and dynamically handled the column names based on the template structure defined in the backend and populated the text maintained in the data elements. This will enable them to read the translated texts as well. The translation process is different in ABAP Cloud; hence, the traditional approach does not work.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

​ IntroductionIn continuation to my previous blog post (How to perform mass upload from an Excel file in a FIORI App in SAP BTP ABAP Environment – Part 1), in this blog post, I will showcase an easy way to extend a FIORI Elements-based OData V4 application in Business Application Studio (BAS) to add a custom action on the responsive table header of the list report page. This custom action will allow the user to open a dialog to browse local files and upload an Excel file. In this blog post, I will be creating a Fiori Elements-based application using the tools provided in SAP Business Application Studio (BAS) and will be extending generated apps using App Extensions to trigger OData V4 operations (in this example, RAP Static Action ‘fileUpload’ to upload an Excel file and RAP Static Function ‘downloadFile’ to download an Excel template).PrerequisitesSAP Business Application Studio (BAS) subscription and developer role in your respective BTP Subaccount.Basic knowledge of performing App Extensions to extend generated FIORI Elements based apps.Basic UI5 skill.Use of File Uploader control.Basic of OData V4 OperationFunction – Functions are operations exposed by an OData service that don’t have side effects. Functions must return data and can include additional path segments. Functions are invoked using HTTP method GET.Action – Actions are operation exposed by an OData service that can have side effects. Actions can return data but must not be composed with additional path segments. Actions are invoked using HTTP method POST.Operation – Both Functions and Actions are operations that can return data. Operations are either bound to a resource (for example, an entity type), that makes them members of that instance type. Operations can also be unbound. Unbound operations are called as static operations (using “action imports” or “function imports”) since a static (unbound) operation can’t be called directly.The OData V4 model supports OData operations (Action Import, Function Import, Bound Actions and Bound Functions).RAP – Non-Standard OperationsNon-standard operations are RAP BO Operations that provide transactional behavior and that are user defined and implemented. There are two kinds of non-standard operations.Action: User-implemented operations that change the data of a BO instance. They are self-implemented operations. Two main categories of actions can be implemented in RAP:Non-factory actions: Defines a RAP action which offers non-standard behavior. The custom logic must be implemented in the RAP handler method FOR MODIFY. An action per default relates to a RAP BO entity instance and changes the state of the instance. An action is related to an instance by default. If the optional keyword static is used, the action is defined as static action. Static actions are not bound to any instance of a RAP BO entity but relate to the complete entity.Factory actions: Factory actions are used to create RAP BO entity instances. Factory actions can be instance-bound (default) or static. Instance-bound factory actions can copy specific values of an instance. Static factory actions can be used to create instances with prefilled default values.Function: User-implemented operations that return data without any side effects.Instance Function: Bound to an instance of a RAP BO Entity which can have input parameters.Static Function: Not bound to any instance of a RAP BO Entity but relate to the complete entity. It can also have input parameters.Can we create unbound OData V4 operations “Action Import” or “Function Import” in RAP?At this moment, we cannot implement unbound function and action in RAP. Both the static action and functions are related to the complete entity. But it is supported in CAP.Code sample:How RAP Static Action and Functions are related to the complete entity?Let’s understand from the metadata itself.It is bound to the complete entity and with the collection of the entity. Let’s beginFor the interest of time and topic, I am not showing all the basic steps to create a FIORI Elements based application in BAS. There are multiple blog posts and SAP Learning materials available to refer. I will directly jump to perform app extension.We will be using the Fiori Guided Development tool to add a custom action to a page using extension and will be using the page map to perform List Report page controller extension.Note: When you use the Fiori Guided Development tool to add a custom action in OData V4 based app, the framework automatically adds a custom action handler function in JavaScript creating a new custom JS file but does not perform controller extension. This is the way OData V4 app extension behaves which adds a “controlConfiguration” in the target page.Custom Action in the Table toolbar for the list report in OData V2 vs OData V4:OData V2:OData V4:Further Reference: Adding Custom Actions Using Extension Points But why do we need a controller extension? Can’t we use the custom action handler function created automatically by the Guided Development tool in an OData V4 app?We need to create a controller extension in an OData V4 app explicitly to use the SAP FIORI Elements extension API to open a dialog using the ‘loadFragement’ function; to perform OData operations and to use Edit Flow (A controller extension offering hooks into the edit flow of the application) to invoke action and perform secured execution. Otherwise, within the custom JS file, we won’t get the model reference to trigger the OData operation (here RAP Static Action and Static Function).So, we will start by adding a custom action to the table toolbar using the FIORI Guided Development tool.Step 1: Right click on the ‘webapp’ folder and select ‘Open Guided Development’Step 2: Select ‘Add a custom action to a page using extensions’.Step 3: Follow the guide and select the page and choose a custom controller and function name.Following code snippets to be inserted.Go ahead and insert snippets. This will modify the manifest settings and create custom JS file.Step 4: But we need to perform controller extension. So, the custom JS file can be deleted. Step 5: Now, we will perform the List report controller extension using the page map. So, right click on ‘webapp’ folder and select ‘Show Page Map’.Step 6: From the page map, select the List Report page and click on ‘Show controller extension’. After that, click on ‘Add Controller Extension’ to create controller extension on the list report page.Note: It is advisable to perform controller extensions for the concerned page rather than performing a generic extension for all the list report pages. Here, ‘VendorEmailList’ is the target list report page. So, I selected that option.Step 7: Now, we can navigate to the controller extension using ‘Edit in source code’ button and add custom logic.This the way the controller extension looks.Let us add a custom handler function ‘uploadMailDialog’ to handle the custom action.After the controller extension, the following changes applied to the manifest.json file. It signifies a controller extension added to the list report controller.Step 8: Now, we need to tweak the manifest settings in such a way that the custom action triggers the custom handler function ‘uploadMailDialog’ added to the controller extension.We need to add the custom action path adding ‘.extension’ as a prefix. ‘.extension’ refers to the controller extension.Step 9: Now, if we preview the application and click on the custom action button, it will trigger the controller extension.Step 10: Let us now add a fragment to open the file dialog. So, we will use ‘fileUploader’ control. I created a fragment folder inside the ‘ext’ folder and added a new xml file ‘uploadFileDialog.fragement.xml’.Following is the sample code snippet.  <core:FragmentDefinition xmlns:core=”sap.ui.core” xmlns:f=”sap.ui.layout.form” xmlns:u=”sap.ui.unified” xmlns=”sap.m”
xmlns:layout=”sap.ui.layout”>
<Dialog id=”idFileDialog” title=”{i18n>uploadVendorMailDialogTitle}” >

<VBox id=”idVBox” width=”100%”>
<core:InvisibleText id=”idInvisibleText” text=”{i18n>uploadVendorMailDialogTitle}”/>
<f:SimpleForm id=”idSimpleForm” editable=”true” layout=”ResponsiveGridLayout” maxContainerCols=”2″>
<f:content>
<Label id=”idFileUploadlabel” required=”true” text=”{i18n>uploadVendorMailFile}”/>
<u:FileUploader id=”idFileUpload” name=”internalMailFileUpload” change=”onFileChange” width=”100%”
uploadComplete=”onUploadComplete” style=”Emphasized” fileType=”xls,xlsx” placeholder=”{i18n>uploadVendorMailPlaceholder}”
tooltip=”{i18n>uploadVendorMailTooolTip}” sendXHR=”false” />
</f:content>
</f:SimpleForm>
</VBox>
<footer>
<Toolbar id=”idFooterToolbar”>
<content>
<Button id=”idDownloadTempButton” text=”{i18n>downloadTempButtonTxt}” press=”onTempDownload” icon= “sap-icon://download-from-cloud” />
<Button id=”idUploadButton” text=”{i18n>uploadButtonTxt}” type=”Emphasized” press=”onUploadPress” icon=”sap-icon://upload-to-cloud” />
<Button id=”idCancelButton” text=”{i18n>cancelButtonTxt}” press=”onCancelPress” icon=”sap-icon://cancel”/>
</content>
</Toolbar>
</footer>
</Dialog>
</core:FragmentDefinition>  Now, add the logic to open the dialog on click of the action similar to the code snippet mentioned below.   uploadMailDialog: function (oEvent) {
this.base.getExtensionAPI().loadFragment({
name: Constants.fragmentName,
type: “XML”,
controller: this
}).then(function (oDialogResult) {
oDialog = oDialogResult;
oDialogResult.open();
})
},  Now, in application preview the app looks like below.Step 11: So, till now, on click of the action ‘Upload’, a dialog will open up asking the user to upload a file. The button ‘Template’ will allow users to download an Excel template, and the button ‘Upload’ will allow users to upload the given file.Let us add logic to read the file content, mimetype and filename of the given file. I used ‘onFileChange’ event of the File uploader control as highlighted below.   // On File Change
onFileChange: function (oEvent) {
// Read file
var file = oEvent.getParameter(“files”)[0];
if (file === undefined) {
return;
}
fileType = file.type; //mimetype or file type
fileName = file.name;
//Instantiate JavaScript FileReader API
var fileReader = new FileReader();
//Read file content using JavaScript FileReader API
var readFile = function onReadFile(file) {
return new Promise(function (resolve) {
fileReader.onload = function (loadEvent) {
resolve(loadEvent.target.result.match(/,(.*)$/)[1]);
};
fileReader.readAsDataURL(file);
});
};

new Action(readFile(file)).executeWithBusyIndicator().then(function (result) {
fileContent = result;
})
},  Here, I used JS FileReader API to read the file content as data URL and later used RegEx to clean the base64 encoded file content. Step 12: Now, implement the logic to upload a file once the ‘upload’ button is clicked.   //perform upload
onUploadPress: function (oEvent) {
var oResourceBundle = this.base.getView().getModel(“i18n”).getResourceBundle();
//check file has been entered
if (fileContent === undefined || fileContent === “”) {
MessageToast.show(oResourceBundle.getText(“uploadFileErrMsg”));
return;
}

var oModel = this.base.getExtensionAPI().getModel();

var oOperation = oModel.bindContext(“/VendorEmail” + Constants.serviceNamespace + “fileUpload(…)”);

var fnSuccess = function () {
oModel.refresh();
MessageToast.show(oResourceBundle.getText(“uploadFileSuccMsg”));
oDialog.close();
//Clear the file name from file uploader
sap.ui.getCore().byId(“idFileUpload”).clear();
oDialog.destroy();
fileContent = undefined;
}.bind(this);

var fnError = function (oError) {
this.base.editFlow.securedExecution(
function () {
Messaging.addMessages(
new sap.ui.core.message.Message({
message: oError.message,
target: “”,
persistent: true,
type: sap.ui.core.MessageType.Error,
code: oError.error.code
})
);
var aErrorDetail = oError.error.details;
aErrorDetail.forEach((error) => {
Messaging.addMessages(
new sap.ui.core.message.Message({
message: error.message,
target: “”,
persistent: true,
type: sap.ui.core.MessageType.Error,
code: error.code
})
);
})
}
);
oDialog.close();
//Clear the file name from file uploader
sap.ui.getCore().byId(“idFileUpload”).clear();
oDialog.destroy();
fileContent = undefined;
}.bind(this);

oOperation.setParameter(“mimeType”, fileType);
oOperation.setParameter(“fileName”, fileName);
oOperation.setParameter(“fileContent”, fileContent);
oOperation.setParameter(“process”, sProcess);
oOperation.execute().then(fnSuccess, fnError);
},  Here, I triggered OData V4 operation a bound action (RAP ‘fileUpload’ Static Action) passing File Type, File Name and File Content as parameters.Earlier, I already explained that a RAP Static Action or Function is bound to a collection specified by an OData entity set. So, to call such actions or functions, you can create a context binding with an absolute path, or with a relative path for the operation (for example odata.srv.name.space.staticMyAction(…)”) and the header context of a list binding as parent context. The following sample shows a button press event handler which calls the static MyAction action on the MyEntity entity set. var oModel = this.getView().getModel();

oModel.bindContext(“/MyEntity/odata.srv.name.space.staticMyAction(…)”).invoke(); The same example with a relative binding and the header context of the list binding as parent context: var oModel = this.getView().getModel(),
// assume there is a table with ID “idMyEntityTab” and its items aggregation bound to “/MyEntity”
oListBinding = this.byId(“idMyEntityTab”).getBinding(“items”),
oHeaderContext = oListBinding.getHeaderContext();

oModel.bindContext(“name.space.staticMyAction(…)”, oHeaderContext).invoke(); Note: In my code sample, I am using “execute” function of the OData V4 Context binding. But, from UI5 version 1.123.0 onwards, “execute” function is deprecated. So, use “invoke” function instead as shown in the example. Simply, replace “execute’ function with “invoke”.  oOperation.setParameter(“mimeType”, fileType);
oOperation.setParameter(“fileName”, fileName);
oOperation.setParameter(“fileContent”, fileContent);
oOperation.setParameter(“process”, sProcess);
oOperation.invoke().then(fnSuccess, fnError); Reference: Execute function of OData V4 Context Binding API Alternatively, you can also use the “invokeAction” method of the Edit Flow API ( Edit Flow API ). However, I found the above approach to be the preferred choice.Reference: How to call RAP static action from elements FPM extension with EditFlow.invokeAction()? After this step when you will execute the ‘upload’ action selecting an Excel file to upload, the RAP static action ‘fileUpload’ will trigger and the XCO XLSX module will do the rest to parse the file content and create/update BO entity instances as explained in my previous blog post.Step 13: Similar to the file upload, the excel template can also be downloaded invoking RAP static function ‘downloadFile’.Sample code snippet: onTempDownload: function (oEvent) {
var oModel = this.base.getExtensionAPI().getModel(),
oResourceBundle = this.base.getView().getModel(“i18n”).getResourceBundle();

var oModel = this.base.getExtensionAPI().getModel(),
oResourceBundle = this.base.getView().getModel(“i18n”).getResourceBundle();

var oOperation = oModel.bindContext(“/VendorEmail” + Constants.serviceNamespace + “downloadFile(…)”);

//Success function to display success messages from OData Operation
var fnSuccess = function () {
var oResults = oOperation.getBoundContext().getObject();

var aUint8Array = Uint8Array.from(atob(oResults.fileContent), c => c.charCodeAt(0)),
oblob = new Blob([aUint8Array], { type: oResults.mimeType });

File.save(oblob, oResults.fileName, oResults.fileExtension, oResults.mimeType);
MessageToast.show(oResourceBundle.getText(“downloadTempSuccMsg”));
}.bind(this);

//Error function to display error messages from OData Operation
var fnError = function () {
this.base.editFlow.securedExecution(
function () {
Messaging.addMessages(
new sap.ui.core.message.Message({
message: oError.message,
target: “”,
persistent: true,
type: sap.ui.core.MessageType.Error,
code: oError.error.code
})
);
var aErrorDetail = oError.error.details;
aErrorDetail.forEach((error) => {
Messaging.addMessages(
new sap.ui.core.message.Message({
message: error.message,
target: “”,
persistent: true,
type: sap.ui.core.MessageType.Error,
code: error.code
})
);
})
}
);
}.bind(this);

// Execute OData V4 operation i.e a static function ‘downloadFile’ to download the excel template
oOperation.execute().then(fnSuccess, fnError)
// From UI5 version 1.123.0 onwards use invoke function
//oOperation.invoke().then(fnSuccess, fnError); Please stay tuned for the next blog post where I will explain how I used the XCO XLSX module to create an Excel template and dynamically handled the column names based on the template structure defined in the backend and populated the text maintained in the data elements. This will enable them to read the translated texts as well. The translation process is different in ABAP Cloud; hence, the traditional approach does not work.                          Read More Technology Blogs by SAP articles 

#SAP

#SAPTechnologyblog

You May Also Like

More From Author

+ There are no comments

Add yours