Connecting a Custom SAP UI5 Control to OData Services

Connecting a Custom SAP UI5 Control to OData Services

Published: 18/January/2021

Reading time: 7 mins

By Cord Jastram, Software Developer at DXC.

SAPUI5 offers a large set of controls ranging from simple ones like the Label to complex controls like the PlanningCalendar control. The available set of SAPUI5 controls covers most use cases for SAPUI5, but sometimes the requirements for a developer go beyond the scope of the existing controls. One example of these advanced use cases is when you want to edit and annotate PDF files using a SAPUI5 application. There is a control available from SAP for viewing PDF files, but it is a viewing only control. But SAPUI5 has a feature that comes to the rescue: SAPUI5 Custom controls.

The custom control approach allows a developer to create a bespoke SAPUI5 control in such a way that it plays nicely with the SAPUI5 framework. There are two different approaches to create custom controls. The first approach builds a control from scratch, whereas the second approach wraps an already existing non-UI5 control. In my article, I show an example for the second approach, where I wrap a commercial JavaScript-based control for editing PDF files.

I wrap a control named WebViewer, which is developed by PDFTron, a company from Canada. WebViewer is a JavaScript library for viewing, annotating, and editing PDF documents. Wrapped as a SAPUI5 custom control, it allows the utilization of two interactive elements of PDF files, namely annotations and form-fields, in a SAPUI5 application. But the real value emerges when the data of a PDF file can also be processed in an SAP system, and therefore, I also implement an OData interface for PDF files and their annotations and form data.

I will not cover every detail of my SAPUI5 application in this article as it covers a lot of SAPUI5 development tasks, but the source code of the client and the source code of the OData services are available at GitHub. This allows a developer to set up the solution for testing purposes and to have a closer look at details of the implementation.

This is a series of articles consisting of three major parts. In this first part, I show a sample application that demonstrates the use of the new custom control. In the second part, I will show how the WebViewer control is wrapped as a SAPUI5 custom control. In the third and last part, I will develop a set of OData services that allows to read and update PDF files on the server from my SAPUI5 application. You can read the other part by clicking the following links:

PART 3: Connecting the custom control to OData services

The custom control and OData Services

In the third part, I show how I connect the custom control to OData services in order to store PDF files and the associated annotations and form fields in the SAP database.

For the actual file data, I have added a function to the PDFEditor class, which returns the actual file data of the PDF file shown in the WebViewer control, and there is an option to get the flattened version of the file.

For the form fields, I assume that they have been added to the PDF file in advance, and afterward, this file is filled out by another person. This person only changes the values of the fields but does not add or remove fields. To handle this scenario, I save all form fields in the database when the PDF file is saved the first time by an end-user. After this initial action, I only update form field values, which have been changed. To implement this scenario, I have added a function getFormFields(documentId, onlyModified) to my custom control class, which depending on the parameter onlyModified, returns an array of all form fields or only the modified ones. For each file, I store a flag if the initial creation of the form fields in the database table has been performed.

For the annotations I assume, that they can be added, modified, and deleted. The WebViewer control allows to add an event handler, which is called whenever an annotation is added, modified, and deleted. Using this event handler, I track all actions regarding annotations, and this allows me to implement a function getAnnotations(documentId), which returns a dictionary with three arrays containing new, modified, and deleted annotations.

Now the data which should be stored in the SAP system is available, and the OData services can be developed.

The OData services

At first, I define a simple data model, as shown in Figure 11. The main entity is PdfFile, and there is an entity PdfFormField for the form data and an entity PdfAnnotation for the annotation. Both entities are related to the PdfFile entity by a 0…N relation. For simplicity, I have omitted the actual fields of the tables.

Figure 11: Data model of the application

For each entity, I create a database table in the SAP system. I store the PDF files in a table named ZPDF_FILE, the annotations in a table named ZPDF_ANNOTATION, and the form fields in the table ZPDF_FORM_FIELD. You can see the table definitions and in Figure 12

Figure 12: Database table definitions for storing PDF files, annotations, and form fields

The first key field ID of each table is a UUID (Universal Unique Identifier). The field current_editor in table ZPDF_FILE has been included in the database table, although it is a transient field that stores no data in the database. When I read the PDF file data from the database, the value of current_editor is set to the name of the currently logged-in user, and this name is used as the author for annotations.

For the annotations, I have included the fields hierarchy_level and drill_state that are required to show the annotations in a TreeTable control. I also have included the page number and the y-position of an annotation as fields. This allows to show them in the TreeTable in the order in which they appear on each page.

Service Implementation

Creating the OData service definition is done in a straightforward way. For each table, I create an OData entity using SAP transaction code SEGW by importing the table as a DDIC structure. Furthermore, I setup the associations of the different entities, as shown in Figure 13. For the details of creating OData services from a DDIC structure, see the blog from Andre Fischer.

Figure 13: OData model in transaction SEGW

For each entity in my OData model, I create a data access class that performs the database operations for each table. The data access object (DAO) pattern helps to separate the low-level database access from the high-level business services. It also simplifies changes to the storage mechanism of the PDF files. For example, when at a later stage, the storage of the PDF file should be moved to the file system, I only change the implementation of the DAO class. Using this approach, I have implemented the SAP OData methods shown in Table 1 for each entity.

Table 1: OData methods

Now the building blocks for connecting the custom control to the OData services are ready to be used.

Reading a PDF file via OData

In a UI5 application, reading data via OData is triggered by data binding. Data binding can be done declaratively in an XML view or programmatically by JavaScript code. For displaying a PDF file, as shown in Figure 5, both approaches are used. In this case, a PdfFile entity is bound to the view via JavaScript, and then the annotations and the form fields are bound via a path declaration in the XML definition. When I look at the network tab of the debugger of my Chrome browser, I see the OData requests, which are executed when a web page shown in Figure 5 is loaded. After reading the file data, the form field data and the annotation data are retrieved without any manual coding. As all OData operations have been implemented correctly, everything works fine.

Figure 14: Network requests for reading a PDF file and the associated form fields and annotation via OData

Updating the PdfFile entity via OData

Listing 11 shows the saveFile() function, which is called after the Save button of the dialog shown in Figure 4 is pressed.

Listing 11: Save the PdfFile entity

I perform nine major steps.

  1. Close the SaveFile dialog
  2. Get the data of the input fields of the dialog
  3. Get the OData model and the current PdfFile entity
  4. Get the annotations and form fields
  5. Get the modified file data from my custom control. Depending on the parameter flattenFile, I get a flattened version.
  6. Create the modified entity PdfFile entity.
  7. Define the function which is called when the update succeeds
  8. Define the function which is called when the update fails
  9. Perform the update

The actual calls of the OData services are implemented by a class PDFDataService. The actual implementation of each method is surprisingly simple. Update a PdfFile entity is done with just 8 lines of source code (Listing 12).

Listing 12: Updating a PdfFile entity

Updating or creating the form fields is only slightly more complicated, as can be seen from Listing 13

Listing 13: Updating form fields

Updating the annotations, which is not shown here, is only slightly more complicated as there are also annotations that have to be deleted but this only increases the number of JavaScript code lines and not the overall complexity.

Conclusion

This series has given a very condensed description of the creation of a SAPUI5 custom control. I have used a lot of different and sometimes lesser-known features of the SAPUI5 framework and of the JavaScript language. However, the amount of code I had to write compared to the additional functionality for handling PDF files is surprisingly small.

The custom control works nicely with the SAPUI5 framework. It is configured in an XML view and needs no extensive additional JavaScript coding. The custom control shown in this article does not offer a complete interface to all WebViewer functionality. But it is an excellent starting point for implementing PDF-based SAPUI5 applications and for extending the custom control with more WebViewer features.

The author is grateful to Andrey Safonov from PDFTron for his dedicated support.

More Resources

See All Related Content