cancel
Showing results for 
Search instead for 
Did you mean: 

Handling attachments in MDK app

Former Member
0 Kudos

Hello all,

I have tried to follow the details provided by Ashish Jain in the link

https://answers.sap.com/questions/13151554/sap-mdk-fetch-attachments-from-backend-through-oda.html

for handling attachments in my MDK offline application for creating notifications. I have some doubts:

I am using CreateEntity and then CreateRelatedMedia(if attachment exists) actions to create my notifications. At first, I am not capturing the file names of the attachments user uploads, but just capture the value for the Attachment control in the Media property. To try, I tried to pass property FileName, but then also it did not help.

Below are my actions code:

CreateEntity action:

{
    "ActionResult": {
        "_Name": "create"
    },
    "OnFailure": "/ZME_CREATENOTIFICATION/Actions/CreateNotificationEntityFailureMessage.action",
    "OnSuccess": "/ZME_CREATENOTIFICATION/Rules/CheckIfAttachmentExists.rule.js",

    "Headers": {
        "OfflineOData.RemoveAfterUpload": "true"
    },

    
"Properties": {
        "Description": "#Page:NotifCreatePage/#Control:DescFormCellSimpleProperty/#Value",
        "Equipment":  "/ZME_CREATENOTIFICATION/Rules/EquipInLPOrSP.rule.js",
        "FuncLoc": "/ZME_CREATENOTIFICATION/Rules/FuncLocInLPOrSP.rule.js",
        "NotifType": "#Page:NotifCreatePage/#Control:NotTypeFormCellListPicker/#SelectedValue",
        "Priority": "#Page:NotifCreatePage/#Control:PriorityFormCellListPicker/#SelectedValue",
        "ShortText": "#Page:NotifCreatePage/#Control:ShortTextFormCellSimpleProperty/#Value"
    },
    
    "ShowActivityIndicator": true,
    "Target": {
        "EntitySet": "PMNotificationSet",
        "Service": "/ZME_CREATENOTIFICATION/Services/ZME_PM_CREATE_NOTIFICATION_SRV.service"
    },
    "_Type": "Action.Type.ODataService.CreateEntity"
}

CreateRelatedMedia action:

{


    "IsOnlineRequest": false,
    "Media": "#Page:NotifCreatePage/#Control:AttachmentNotifFormCell/#Value",
    "OnFailure": "/ZME_CREATENOTIFICATION/Actions/CreateNotificationFailureMessage.action",
    "OnSuccess": "/ZME_CREATENOTIFICATION/Actions/CreateNotificationSuccessMessage.action",
    "Headers": {
        "OfflineOData.RemoveAfterUpload": "false"
    },
    "ParentLink": {
        "Property": "NotifToAttachmentNav",
        "Target": {
            "EntitySet": "PMNotificationSet",
            "ReadLink": "#ActionResults:create/#Property:data/#Property:@odata.readLink"
        }
    },
    "Properties": {
        "Notification": "#ActionResults:create/#Property:data/#Property:Notification",
        "FileName": "AttachPic"


    },
    "Target": {
        "EntitySet": "PMAttachmentSet",
        "Service": "/ZME_CREATENOTIFICATION/Services/ZME_PM_CREATE_NOTIFICATION_SRV.service"
    },
    "_Type": "Action.Type.ODataService.CreateRelatedMedia"
}
 

Now, these notifications, both synced and unsynced to the backed are to be displayed in the Notifications list page. user can edit the non synced notifications and just view the synced ones. My issue is showing the attachments, by clicking on the ObjectCell where I show my notifications.How is the file name to be handled, when the file is uploaded using FormCell.Attachment control? I see that we need the file name in the DownloadOrOpenDocument.js. (const filename = bindingObject.FileName+".jpg";)

let fs = require('tns-core-modules/file-system');
export default function DownloadOrOpenDocument(sectionedTableProxy) {
  const pageProxy = sectionedTableProxy.getPageProxy();
  const bindingObject = pageProxy.getActionBinding();
  const readLink = bindingObject["@odata.readLink"];
  const filename = bindingObject.ProductId+".jpg";
  const serviceName = "/MDKMedia/Services/MDKMedia.service";
  const entitySet = 'Products';
  // first we need to decide if the media exists locally or needs to be downloaded
  return sectionedTableProxy.isMediaLocal(serviceName, entitySet, readLink).then((mediaIsLocal) => {
    const tempDir = fs.knownFolders.temp();
    const attachmentPath = fs.path.join(tempDir.path, filename);
    const fileExists = fs.File.exists(attachmentPath);
    if (mediaIsLocal && fileExists) {
      // the media has been downloaded, we can open it -> the path needs to be provided in the action definition
      // or it should come from binding
      // persist the media data to local sandbox, so we can open it with the document viewer
      if (!bindingObject) {
        return pageProxy.executeAction("/MDKMedia/Actions/ODataDownloadFailure.action");
      }
      if (readLink) {
          pageProxy.setActionBinding({
            'FileName': attachmentPath
          });
          return pageProxy.executeAction("/MDKMedia/Actions/OpenRelatedDocument.action")
      }
    } else if (mediaIsLocal) {
      //  The media is on the offline store but the file hasn't been saved
      if (pageProxy.getClientData()[readLink]) {
        // we have the stream in client data, save and open
        const attachmentFile = fs.File.fromPath(attachmentPath);
        attachmentFile.writeSync(pageProxy.getClientData()[readLink], function (err) {
          attachmentFile.remove();
          return pageProxy.executeAction('/MDKMedia/Actions/ODataDownloadFailure.action');
        });
        pageProxy.setActionBinding({
          'FileName': attachmentPath
        });
        return pageProxy.executeAction('/MDKMedia/Actions/OpenRelatedDocument.action');
      } 
    } 
    else {
      if (sectionedTableProxy.downloadInProgressForReadLink(readLink)) {
        return Promise.resolve();
      } else {
        return sectionedTableProxy.executeAction("/MDKMedia/Actions/DownloadDocumentStreams.action").catch(error => {
        });
      }
    }
  });
}

Please help.

Thanks

Aanchal

Jitendra_Kansal
Product and Topic Expert
Product and Topic Expert
0 Kudos

Were you able to resolve your issue? If any of the below responses helped you, then please mark the helpful answer by accepting it OR post an answer so others might benefit from your findings and then close this discussion.

Regards,

Jitendra (SAP Community Moderator)

Accepted Solutions (0)

Answers (1)

Answers (1)

bill_froelich
Product and Topic Expert
Product and Topic Expert
0 Kudos

"My issue is showing the attachments, by clicking on the ObjectCell where I show my notifications.How is the file name to be handled, when the file is uploaded using FormCell.Attachment control?"

The filename is the full path to the attachment on the local device storage.

The sample rule DownloadOrOpenDocument actually handles three scenarios using the following logic

  1. Check if the media is local - This is checking the offline store to see if the media associated with the entity already exists in the local store. For an attachment created locally this will always be true since the entity was created on the device.
  2. Check if file exists locally - The second check is to see if the file exists locally on the device storage (outside of the offline store).
  3. If the media is local and the file exists the rule can just pass the full path to the file in the FileName property to the OpenRelatedDocument action which will display the file (assuming the format can be handled by the device document viewer)
  4. If the media is local but the file does not exists locally on the device. It will write the file contents from the offline store to a local file first and then call the OpenRelatedDocument action again passing the full path to the file
  5. If the media is not local - Which would be something attached in the backend but where the media object does not exist in the local store at this time. In this case the rule will call Download Media to retrieve the media from the oData service into the offline store. It will update the progress indicator on the object cell showing that it is downloading. Once downloaded the indicator will change and the user can re-press the cell again to open the attachment.

--Bill

Former Member
0 Kudos

Hello Bill,

Thanks for your detailed explanation. I have the below issues:

1. filename has to be dynamic(below code line), based on extension of the file uploaded. As I am not capturing the file name when uploading, maybe I have to do that using "OnValueChange" event of the attachment control.

const filename = bindingObject.ProductId+".jpg";

2. If the media is local but the file does not exists locally on the device. It will write the file contents from the offline store to a local file first and then call the OpenRelatedDocument action again passing the full path to the file I understand below code section handles this

   else if (mediaIsLocal) {
        //  The media is on the offline store but the file hasn't been saved
        if (pageProxy.getClientData()[readLink]) {
          // we have the stream in client data, save and open
          const attachmentFile = fs.File.fromPath(attachmentPath);
          attachmentFile.writeSync(pageProxy.getClientData()[readLink], function (err) {
            attachmentFile.remove();
            return pageProxy.executeAction('/ZME_CREATENOTIFICATION/Actions/ODataDownloadFailure.action');
          });
          pageProxy.setActionBinding({
            'FileName': attachmentPath
          });
          return pageProxy.executeAction('/ZME_CREATENOTIFICATION/Actions/OpenRelatedDocument.action');
        } 
      } 

pageProxy.getClientData()[readLink] remains undefined for me. I am not sure if I have to set the ClientData in this case, at the time of uploading the attachment using "OnValueChange" event of the attachment control?

Thanks and regards

Aanchal

bill_froelich
Product and Topic Expert
Product and Topic Expert
0 Kudos

The filename passed to the action needs to be the full path to the file as it exists on the device. In the rule it gets the path to the temp directory on the device and then appends the filename to create the attachment path. This value is set as the file name in the binding object when calling the actions. So while it is called FileName in the binding object it is actually the attachment path being use.

This rule is attached to the OnAccessoryButtonPress. If you are trying to use this in a OnValueChange you may need to adjust the rule since the context will be different.

I also would probably not use OnValueChange to do the upload but rather do it in the Save action for the page but it all depends on your use case.

--Bill