SAPUI5, SAP Cloud Application Programming Model

Upload attachments in a Freestyle UI5 and CAP app


Uploading files in UI5 requires a bit more effort compared to adding an input field on a view. Depending on the backend some specific configuration is needed. In CAP for example, we need to create an entry for each file before we can start uploading.

Sometimes the business scenario requires different flows for uploading files, eg.: when creating new data objects in the backend, you need to create that data object first before you can upload a file that’s related to that data object. The file needs an id of the data object to know where to upload to.

Therefore, I’m sharing one of the most common cases of uploading files with UI5 freestyle and CAP without draft. Why am I referring to draft? In case draft would be enabled, you could directly upload to the draft entry. Without draft, you cannot upload without having an ID of the created entry and therefore have to save the data before you can trigger the upload.

As an example I created a small CAP project with one Book entity and one Attachment entity. The Book and Attachment entity have a relationship to connect attachments to books. On top of that CAP project, I have a UI5 app running for creating a Book with an attachment in one go.

Data model – Backend (CAP)

The data model is quite simple for this demo project, just two entities:

  • Books entity for storing books with a relationship to Attachments
  • Attachments entity for storing attachments that has a relationship to Books and is configured to upload files to.

The relationship will allow us to upload attachments that are related to a book.

OData config in UI (UI5)

Let’s start with the beginning by configuring the OData model in the UI5 app to have full control of the submitChanges function. Therefore, we need to configure it with the type API and use this group id as UpdateGroupId:

UploadSet – View (UI5)

Next we start adding the UploadSet to the view and bind it to the entity for Attachments. The binding is not necessarily needed for the creation/uploading files, this is rather in case of displaying the attachments. The front slash is not here as we use the association for showing the attachments, we only want to show the attachments for the book that’s shown in the detail page.

A few properties need to be changed to make it work with CAP without using drafts:

  • instantUpload: First of all, “instantUpload” needs to be disabled. By disabling this, the file is not immediately uploaded when you select it from your computer. This should only happen after the main Book entry is created. Without the Book entry, we have no book ID and can’t upload it as an attachment for that book.
  • httpRequestMethod: The property “httpRequestMethod” needs to be adapted to the value “Put” because the real upload to CAP will be done during a PUT operation and not during POST.

All the other properties can be configured as you want:

Initialization entry – Controller (UI5)

That’s it for the view, all other magic happens in the controller starting in the onRouteMatched function. This will be used for creating a new binding context for book using the same groupId as defined in the manifest. The bindingcontext will be connected to the view to capture all input fields from the view when saving to the backend.

I also create a binding context for creating attachments when saving a book. We could also create this on the fly. It is just an example how the keep alive context can be used.

Save book and upload attachment – Controller

The “onSave” function, which will be called when they click on Create button in the view, will do the following:

  • trigger a submitBatch using the group id “bookcreate”. This will create an entry for books with the values bound to the view.
  • If successful, the function “createAttachmentEntry” will be triggered for all newly added attachments of the uploadset and wait for the function to create an entry for each file in the attachment entity (just a create, not yet an upload)
  • A second submitBatch will be triggered again for the group id “bookcreate” to send the created attachment entries to the backend. In this case, it will contain only requests to create attachment entries.
  • After the second submitBatch, the function “createAttachmentEntry” will be resolved and start the upload of the files through a PUT operation
  • The upload success message will be called once the upload is completed in the “onUploadCompleted” eventhandler. In case no files are selected, this will never be reached. Therefore, we need to call the success function if no files are selected

The order of the code is not following the logical sequence because the upload of attachments (4) waits for the creation of Attachment (2) which waits for a submitBatch (3) to be resolved.

The “createAttachmentEntry” will be used to create an entry for each file in the attachment entity using the book ID. The book is saved first to have this id, otherwise we wouldn’t be able to create the attachment for it. The function does the following:

  • Create an entry for attachment with the book ID (which is created with the first submitBatch) and the filename. (It uses the binding created in the onRouteMatched function)
  • Wait for the attachment to be created. The create itself will be triggered with the second submitBatch in the “onSave” function (3).
  • Once created, it will set the upload url with the ID of the created attachment

(The check for localhost is just to make the upload work in my local IDE, this should be handled by the UI5 tooling. As I’m using the cds-plugin for running ui5, I’m not able to configure this in the ui5 tooling. )

In the “onUploadCompleted” function we call the success function. Once this function is reached, we are sure that the creation of the book is completed and the uploads are completed. It won’t be reached when no files are being uploaded, that’s why we need to call the success function also in the “onSave” (5) when no attachments are being uploaded.

The onUploadCompleted will be called for every attachment. We need to check if all attachments are being uploaded to avoid having multiple success messages:

The success function showing a messages that the book is created and the list of book is being updates.

Upload finished

And that’s how to use the UploadSet in a UI5 freestyle app using TypeScript on top of CAP.