Skip to Content
Technical Articles
Author's profile photo Sumit Kumar Kundu

Fiori Elements List Report – display and upload images (final part)

In the last blog post I discussed about how we can display images in a list report column in an app created using Fiori elements. In this blog, we shall discuss about how to upload image.

Before going through this blog post, it is recommended to read the previous one.

Sample source code for frontend app is here.

Scenario:

There will be a button in a column for each article record of the list and user will upload the image from local desktop/client system.

Yes, you guessed it right. The action column is added as an extension to the list report app. Please refer here for more details on how to add a column. We are using responsive table in this blog, but if your app uses a grid table, refer here.

Steps:

  1. Note: Please ensure the project type is smart template in project settings. This is automatically done when the project is created using ‘List report’ template wizard in SAP Web IDE.

Right click on the project -> New -> Extension

Choose ‘List Report Extension’ in ‘Template Selection’ tab and choose ‘Next’:

Choose Entity set from drop down and provide a name for the fragment to be generated.

Choose ‘Next’.

Choose Finish.

This will create a folder ‘ext’ and a fragment upload.fragment.xml within.

Create another file in the folder ‘ext’ : uploadCell.fragment.xml. Refer SAP documentation to understand what is happening.

So two fragments will be created :

 a. sos.uploadimage.ext.view.upload.fragment.xml,

 b. sos.uploadimage.ext.view.uploadCell.fragment.xml

Alos after the list report app is extended, manifest.json file should look like below.

Below are the sample code.

manifest.json 

"sap.ui5": {

...

  "extends": {
			"extensions": {
				"sap.ui.viewExtensions": {
					"sap.suite.ui.generic.template.ListReport.view.ListReport": {
						"ResponsiveTableColumnsExtension|ZCDS_C_SKUIMAGE": {
							"type": "XML",
							"className": "sap.ui.core.Fragment",
							"fragmentName": "sos.uploadimage.ext.view.upload"
						},
						"ResponsiveTableCellsExtension|ZCDS_C_SKUIMAGE": {
							"type": "XML",
							"className": "sap.ui.core.Fragment",
							"fragmentName": "sos.uploadimage.ext.view.uploadCell"
						}
					}
				},
				"sap.ui.controllerExtensions": {
					"sap.suite.ui.generic.template.ListReport.view.ListReport": {
						"controllerName": "sos.uploadimage.ext.controller.ListReportExt",
						"sap.ui.generic.app": {
							"ZCDS_C_SKUIMAGE": {
								"EntitySet": "ZCDS_C_SKUIMAGE"
							}
						}
					}
				}
			}
		},

...
}

 

sos.uploadimage.ext.view.upload.frgament.xml

<core:FragmentDefinition xmlns:core="sap.ui.core" xmlns="sap.m">
	<Column id="ExtensionWizard::ColumnBreakout">
		<Text text="{i18next>action}"/>
		<customData>
			<core:CustomData key="p13nData" value='\{"columnKey": "Test", "columnIndex" : "101"}'/>
		</customData>
	</Column>
</core:FragmentDefinition>

sos.uploadimage.ext.view.uploadCell.frgament.xml

<core:FragmentDefinition xmlns:core="sap.ui.core" xmlns="sap.m" xmlns:u="sap.ui.unified">
	<VBox>
		<u:FileUploader id="fileUploader" name="myFileUpload" tooltip="Upload your file to the local server" uploadComplete="handleUploadComplete"
			uploadAborted="handleUploadAbort" buttonOnly="true" buttonText="Upload Image" sendXHR="true" uploadOnChange="true" useMultipart="false"
			sameFilenameAllowed="true" change="handleValueChange" style="Emphasized" placeholder="Choose a file for Upload...">
			<u:headerParameters>
				<u:FileUploaderParameter name="slug" value="{CatalogSKU}"/>
			</u:headerParameters>
		</u:FileUploader>
	</VBox>
</core:FragmentDefinition>

 

Note: In the sample code, we have used property sendXHR=”true” above.This property is not supported by Internet Explorer 9.  Please adjust code accordingly.

2. We also need to add code to handle the events raised by the control sap.ui.unified.FileUploader introduced in fragment uploadCell.frgament.xml.

A quick way to add extension controller is again to extend using wizard as shown in step 1 but this time choosing ‘Action’ radio button in ‘Extension Settings’ tab of the wizard.

Delete the ‘Action’ part from manifest.json as we are not really using action button feature. [See the source code from github]

Extension wizard will also add another folder ‘controller’ and file ‘ListReportExt.controller.js’ within.

Overwrite the code in this file with the content below. [See the source code from github]

ListReportExt.controller.js

sap.ui.controller("sos.uploadimage.ext.controller.ListReportExt", {
	onInit: function () {
		var oSmartTable = this.byId("listReport");
		oSmartTable.setEnableAutoBinding(true);
		var i18nModel = new sap.ui.model.resource.ResourceModel({
			bundleName: "sos.uploadimage.i18n.i18n"
		});
		this.getView().setModel(i18nModel, "i18next");
		this.oBundle = this.getView().getModel("i18next").getResourceBundle();
	},
	handleValueChange: function (oEvent) {
		this.getView().setBusy(true);
		var oSource = oEvent.getSource();
		if (oSource.getBindingContext().getProperty("CatalogSKU")) {
			oSource.setUploadUrl(oSource.getModel().sServiceUrl +
				oSource.getModel().createKey("/ZCDS_C_SKUIMAGE", {
					CatalogSKU: oSource.getBindingContext().getProperty("CatalogSKU")
				}) + "/to_SOSImage");

		}
		oSource.getModel().refreshSecurityToken();
		oSource.addHeaderParameter(
			new sap.ui.unified.FileUploaderParameter({
				name: "x-csrf-token",
				value: oSource.getModel().getHeaders()["x-csrf-token"]
			}));
	},
	handleUploadComplete: function (oEvent) {
		var sResponse = oEvent.getParameter("response");
		this.getView().setBusy(false);
		if (sResponse) {
			var sMsg = "";
			var sStatus = oEvent.getParameter("status");
			if (sStatus.toString() === "201" || sStatus.toString() === "202") {
				sMsg = this.oBundle.getText("uploadSuccess", [oEvent.getParameter("fileName")]);
				oEvent.getSource().setValue("");
				this.getView().byId("listReport").rebindTable();
			} else {
				sMsg = this.oBundle.getText("uploadError", [oEvent.getParameter("fileName")]);
				oEvent.getSource().setValue("");
			}
			sap.m.MessageToast.show(sMsg);
		}
	},
	handleUploadAbort: function (oEvent) {
		this.getView().setBusy(false);
		sap.m.MessageToast.show(this.oBundle.getText("uploadAborted"));
	},
	
	handleFileSize:function (oEvent) {
		//this.getView().getModel(["i18next]").
		
		var allMessage =  this.getView().getModel("i18next").getResourceBundle().getText("fileSize",[oEvent.getParameter("fileName")]);
		sap.m.MessageBox.show(allMessage, {
			icon: sap.m.MessageBox.Icon.ERROR, // default sap-icon://message-success
			title:  this.getView().getModel("i18next").getResourceBundle().getText("sizeError"), // default
			actions: sap.m.MessageBox.Action.OK, // default
			onClose: null, // default
			styleClass: "", // default
			initialFocus: null, // default
			textDirection: sap.ui.core.TextDirection.Inherit // default
		});
	},
	
	handleTypeMismatch: function(oEvent) {
		
		var allMessage =  this.getView().getModel("i18next").getResourceBundle().getText("fileType",[oEvent.getParameter("fileName")]);
		sap.m.MessageBox.show(allMessage, {
			icon: sap.m.MessageBox.Icon.ERROR, // default sap-icon://message-success
			title:  this.getView().getModel("i18next").getResourceBundle().getText("typeError"), // default
			actions: sap.m.MessageBox.Action.OK, // default
			onClose: null, // default
			styleClass: "", // default
			initialFocus: null, // default
			textDirection: sap.ui.core.TextDirection.Inherit // default
		});

	
	}
});

 

3. Function handleValueChange: This hander is triggered when a file is uploaded from local device. We are setting the uploadURl property of FIleUploader control appropriately to the navigation property to_SOSImage which is actually the media entity type.

4. Functions handleUploadComplete and handleUploadAbort issues appropriate messages if upload succeeds or fails respectively.

5. Now that frontend coding is done, in S/4HANA or backend system, add below code in method /iwbep/if_mgw_appl_srv_runtime~create_stream of DPC_EXT class.

METHOD /iwbep/if_mgw_appl_srv_runtime~create_stream.

*  Local data declaration
    DATA :
      lv_matnr    TYPE matnr,
      lt_tab      TYPE solix_tab,
      lv_buffer   TYPE xstring,
      lv_imagekey TYPE char10,
      lv_filesize TYPE i,
      lv_url      TYPE DIRNAME,
      ls_entity   TYPE zcl_z_images_mpc=>ts_zcds_i_imagetype.

    FIELD-SYMBOLS : <lfs_cat_sku> TYPE zfiap_cat_sku.

    IF iv_entity_name = 'ZCDS_I_IMAGEType'. " Check the Image entity
      READ TABLE it_key_tab INTO DATA(ls_key_tab) WITH KEY name = 'CatalogSKU'.
      IF sy-subrc = 0.

        CALL FUNCTION 'CONVERSION_EXIT_MATN1_INPUT'
          EXPORTING
            input        = ls_key_tab-value
          IMPORTING
            output       = lv_matnr
          EXCEPTIONS
            length_error = 1
            OTHERS       = 2.
        IF sy-subrc = 0.

        ENDIF.

*     Select Catalog details for the article
        SELECT SINGLE * FROM zfiap_cat_sku
               INTO @DATA(ls_cat_sku)
               WHERE matnr = @lv_matnr.
        IF sy-subrc = 0.
          ASSIGN ls_cat_sku TO <lfs_cat_sku>.

          ls_entity-catalogsku    = <lfs_cat_sku>-matnr = lv_matnr.
          ls_entity-catalogitmkey = <lfs_cat_sku>-ctlg_itm_key.
          ls_entity-mime_type     = <lfs_cat_sku>-mime_type = is_media_resource-mime_type.

          SHIFT lv_matnr LEFT DELETING LEADING '0'.
*          Pass the image name
          <lfs_cat_sku>-image_name = ls_entity-image_name = iv_slug.

******************* IMPORTANT READING ******************************************************
**Generate a unique key for the image_key field here and assihm to <lfs_cat_sku>-image_key
**Below code saves the file in application directory, but adjust the code according 
**to your own content repositiry needs
********************************************************************************************


*          Convert rawstring to binary format
          CALL FUNCTION 'SCMS_XSTRING_TO_BINARY'
            EXPORTING
              buffer        = is_media_resource-value
            IMPORTING
              output_length = lv_filesize
            TABLES
              binary_tab    = lt_tab[].

          lv_imagekey = <lfs_cat_sku>-image_key.

*          Get AL11 directory path
          CALL FUNCTION 'ZDIR_INTERFACE_PATH_GET'
            EXPORTING
              im_dirname = 'DIR_INTERFACE'
            IMPORTING
              ex_path    = lv_url.

*          Prepare image URL of AL11 to save in catalog SKU table
          CONCATENATE  lv_url
                       '/SKU_IMAGES/'(001)
                       lv_matnr
                       '_'(002)
                       lv_imagekey
                       INTO <lfs_cat_sku>-image_url.
          ls_entity-imageurl = <lfs_cat_sku>-image_url.

          <lfs_cat_sku>-file_size = lv_filesize.
          IF lt_tab[] IS NOT INITIAL.
*           Upload the image to AL11
            OPEN DATASET <lfs_cat_sku>-image_url FOR OUTPUT IN BINARY MODE.
            IF sy-subrc = 0.
              LOOP AT lt_tab INTO DATA(ls_tab).
                TRANSFER ls_tab TO <lfs_cat_sku>-image_url.
              ENDLOOP.
              CLOSE DATASET <lfs_cat_sku>-image_url.

              CALL FUNCTION 'ENQUEUE_EZFIAP_CAT_SKU'
                EXPORTING
                  mode_zfiap_cat_sku = 'E'
                  mandt              = sy-mandt
                  matnr              = <lfs_cat_sku>-matnr
                  _scope             = '2'
                EXCEPTIONS
                  foreign_lock       = 1
                  system_failure     = 2
                  OTHERS             = 3.
              IF sy-subrc = 0.
*               Modify the catalog AKU table with image URL
                MODIFY zfiap_cat_sku FROM <lfs_cat_sku>.
              ENDIF.

              CALL FUNCTION 'DEQUEUE_EZFIAP_CAT_SKU'
                EXPORTING
                  mode_zfiap_cat_sku = 'E'
                  mandt              = sy-mandt
                  matnr              = <lfs_cat_sku>-matnr
                  _scope             = '3'.

            ENDIF.
          ENDIF.
        ENDIF.
      ENDIF.

*     Copy entity data to entity reference
      copy_data_to_ref( EXPORTING is_data = ls_entity
                        CHANGING  cr_data = er_entity ).

    ENDIF.

  ENDMETHOD.

As mentioned in the previous blog, for simplicity, we are storing the image file in application server directory, but adapt the code according to your own content repository.

6. Test the app by uploading an image from local device.

 

Conclusion

In this blog series, we saw how to display and upload image for each record in a list report created with Fiori Elements List Report Floorplan.

Assigned Tags

      5 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Former Member
      Former Member

      Great ! but I am still wondering if they will bring some kind of annotation to handle this upload stuff too 🙂 Any ways on a serious note I really do not like the CDS with embedded BOPF because that will be phased out with new REST based ABAP programming model already available for PaaS.

      Also adding annotations is like a mashed up junk- the simple this is not good, annotations with CDS

      and smart templates is too nasty and ugly from a developer perspective just my personal opinion.

       

      Thanks,

      Prasenjit

      Author's profile photo Gregor Wolf
      Gregor Wolf

      Dear Sumit,

      what UI5 version have you used in your implementation? I'm following the documentation Adding Custom Actions Using Extension Points to add an upload button to the Table header. The fragment is loaded without any issue when I click this button. But the handleUploadPress function that I've included in the controller of my extension is never called. 

      Best regards
      Gregor

      Author's profile photo Sumit Kumar Kundu
      Sumit Kumar Kundu
      Blog Post Author

      Hello Gregor,

      I had tested this app in S/4HANA on-premise 1709 FPS02, UI5 version 1.52.9.

      I follow your content regularly so can't help more as I know it might be repeating what you might have tried already 🙂 but one thing I always try are the good old tips by DJ.

      Regards,

      Sumit

      Author's profile photo Gregor Wolf
      Gregor Wolf

      Hi Sumit,

      thank you for your quick response. I've solved the issue now by binding the button in code to my handler instead of defining the handler only in the fragment XML.

      Best regards
      Gregor

      Author's profile photo Sumit Kumar Kundu
      Sumit Kumar Kundu
      Blog Post Author

      That's nice. All power to js! 🙂