Technology Blogs by Members
Explore a vibrant mix of technical expertise, industry insights, and tech buzz in member blogs covering SAP products, technology, and events. Get in the mix!
cancel
Showing results for 
Search instead for 
Did you mean: 
oblomov
Active Participant
Welcome to part 11 of this blog series introducing abap2UI5 — an open-source project for developing UI5 apps purely in ABAP.

In the last post, we explored various additional functionalities and external libraries that can be integrated with abap2UI5, but without delving into the technical background. Now, we aim to address that. This blog post will explain how to enhance abap2UI5 with custom functionalities and additional JavaScript logic. It is designed as a step-by-step guide for UI5 developers, demonstrating how they can develop frontend logic and make it accessible for the use in ABAP through abap2UI5 apps.

Check out the the corresponding GitHub repository here.

Blog Series & More

Find all the information about this project on GitHub, stay up-to-date by following on Twitter and explore the other articles of this blog series:







































(1) Introduction: Developing UI5 Apps Purely in ABAP
(2) Displaying Selection Screens & Tables
(3) Popups, F4-Help, Messages & Controller Logic
(4) Advanced Functionality & Demonstrations
(5) Creating UIs with XML Views, HTML, CSS & JavaScript 
(6) Installation, Configuration & Troubleshooting
(7) Technical Background: Under the Hood of abap2UI5
(8) Repository Organization: Working with abapGit, abaplint & open-abap
(9) Update I: Community Feedback & New Features - Sep. 2023
(10) Extensions I: Exploring External Libraries & Native Device Capabilities
(11) Extensions II: Guideline for Developing New Features in JavaScript (this blog post)
(12) Update II: Community Feedback, New Features & Outlook - Jan. 2024

Content


We will start from the ground up, creating a new project and integrating all the essential logic required for frontend and backend communication. Finally, we will adapt this logic into an abap2UI5 artifact and use it in an abap2UI5 app in the backend. The content is structured as follows:

  1. General Idea &  Creating a new UI5 project

  2. Frontend Development (JS)

    1. Creating a new Custom Control

    2. Receiving data in the Frontend (JS) sent from the backend

    3. Invoking backend functions (ABAP) from the frontend

    4. Receiving data in the backend (ABAP) sent from the frontend

    5. Calling frontend functions (JS) from the backend

    6. Optional I: Implementing faceless controls (without UI)

    7. Optional II: Loading external libraries



  3. Backend Development (ABAP)

    1. Migrating frontend logic to the ABAP Backend

    2. Developing an abap2UI5 app using the new Custom Control



  4. Additional Comments

  5. Conclusion


Let’s begin with the first topic:

1. General Idea & Creating a New UI5 Project


This guideline is centered around one core concept: We rigorously encapsulate all JavaScript logic within a Custom Control (CC). This approach offers the following advantages:

  1. It ensures that there are no dependencies outside of the Custom Control. This means we can easily copy and paste it into the backend with confidence in its correct functionality

  2. We can use the properties of the Custom Control in the XML view for data exchange and communication between JS and ABAP. The Event and Data Binding functionality of the UI5 framework ensure smooth operation and ease of use

  3. By avoiding globally defined variables and relying solely on the UI5 API, we guarantee that the application functions correctly even when the UI5 version is updated or when abap2UI5 operates in different contexts, such as the Fiori Launchpad or SAP Build Workzone Service


Let's start by launching your preferred development environment. Here we use Business Application Studio as it comes pre-equipped with UI5 tooling and create a new UI5 freestyle application. Upon completion, your project folder should resemble the following structure:


Structure of a new UI5 Freestyle App



2.1. Creating a new Custom Control (CC)


Numerous guidelines are available for developing Custom Controls in SAP UI5, such as blog articles and documentation. We will now follow the recommended approach from the documentation to create a new Custom Control. This process begins with creating a new folder and file:


New Folder & File for the Custom Control (CC)


Next, copy & paste the following snippet as your starting point:
sap.ui.define("project1/control/MyCustomControl",[
"sap/ui/core/Control"
], (Control) => {
"use strict";

return Control.extend("project1.control.MyCustomControl", {

renderer(oRM, oControl) {
oRM.openStart("div", oControl);
oRM.openEnd();
oRM.write( 'test');
oRM.close("div");
}
});
});

Basic HTML is used for creating a first output. Integrate the new Custom Control (CC) into your XML View by making the following adjustment to the file View1.view.xml:
<mvc:View controllerName="project1.controller.View1"
xmlns:mvc="sap.ui.core.mvc" displayBlock="true"
xmlns="sap.m" xmlns:z2ui5="project1.control">
<Page id="page" title="{i18n>title}">
<content>
<z2ui5:MyCustomControl id="test" />
</content>
</Page>
</mvc:View>

This XML View will be later created in ABAP on the backend, which means any configurations we establish here can be transmitted from the server. Meanwhile, we can use the CC to add all JavaScript logic we later want to process on the frontend.

Now we will extend step-by-step the functionality of this Custom Control.

2.2 Receiving Data in the Frontend (JS) Sent from to the Backend (ABAP)


First we want to send values from ABAP to the frontend Javascript Logic. We start by setting the property value in your XML view (this can later be done in ABAP):
<mvc:View controllerName="project1.controller.View1"
xmlns:mvc="sap.ui.core.mvc" displayBlock="true"
xmlns="sap.m" xmlns:z2ui5="project1.control">
<Page id="page" title="{i18n>title}">
<content>
<z2ui5:MyCustomControl id="test" value="MyBackendValue"/>
</content>
</Page>
</mvc:View>

And we add the property "value" to the Custom Control (together with a setter and getter method) that it can receive its data:
sap.ui.define("project1/control/MyCustomControl",[
"sap/ui/core/Control"
], (Control) => {
"use strict";

return Control.extend("project1.control.MyCustomControl", {
metadata : {

properties : { value: {type : "string" } },

setValue(value){
this.setProperty("value", value);
},
getValue(){
return this.getProperty("value");
},

renderer(oRM, oControl) {
oRM.openStart("div", oControl);
oRM.openEnd();
oRM.write( oControl.getValue("value") );
oRM.close("div");
} }); });

We display the value in the rendering method to verify if it is correctly transmitted to the frontend. When you begin testing this demo in a terminal (using the command 'npm run start'), you should observe the following result:


CC - Displaying values sent from the backend


As you can observe, the values are successfully sent to the Custom Control. This illustrates how we can later transmit data from the backend to the frontend. Just add more properties and retrieve the backend values at the frontend by simply reading the control's property using the following command:
oControl.getValue("value");

Next you can use these backend values to implement your JavaScript logic. But at a certain stage, it will become necessary to interact with the backend again. In the next section, we will trigger backend interactions, by focusing on the use of events in Custom Controls.

2.3 Invoking Backend Functions (ABAP) from the Frontend


In addition to the properties, we can enhance the Custom Control by adding events. We'll setup a "change" event which is triggered after a delay of 5 seconds. Typically, you would implement logic that culminates in triggering an event; however, for this example, we're adopting a straightforward approach to emphasize the underlying principle. The updated snippet for the Custom Control now appears as follows:
sap.ui.define("project1/control/MyCustomControl",[
"sap/ui/core/Control"
], (Control) => {
"use strict";

return Control.extend("project1.control.MyCustomControl", {
metadata: {

properties: {
value: { type: "string" }
},
events: {
change: {}
}
},

setValue(value) {
this.setProperty("value", value);
},
getValue() {
return this.getProperty("value");
},

onAfterRendering() {

setTimeout((oControl) => {
oControl.fireChange();
}, 5000, this);

},

renderer(oRM, oControl) {
oRM.openStart("div", oControl);
oRM.openEnd();
oRM.write(oControl.getValue("value"));
oRM.close("div");
}
});
});

Next, we need to manage the event in our controller, which is a process we will later replicate in ABAP. To begin, make the following adjustments to the XML:
<mvc:View controllerName="project1.controller.View1"
xmlns:mvc="sap.ui.core.mvc" displayBlock="true"
xmlns="sap.m" xmlns:z2ui5="project1.control">
<Page id="page" title="{i18n>title}">
<content>
<z2ui5:MyCustomControl id="test" value="MyBackendValue" change="onMyBackendChangeHandler" />
</content>
</Page>
</mvc:View>

And the following adjustments to the controller:
sap.ui.define("project1/control/MyCustomControl",[
"sap/ui/core/mvc/Controller"
],

function (Controller) {
"use strict";

return Controller.extend("project1.controller.View1", {

onMyBackendChangeHandler: function (){
sap.m.MessageToast.show( 'Frontend Event raised, and processed in the backend');
}
});
});

When we test the application again, we observe that the Control is loaded and, after five seconds, the event is triggered. It is then handled in the controller by displaying a message toast. In a later step, we will implement the call of the message toast in ABAP:


CC - Invoking backend functions from the frontend


We can use this event now and start to process some backend logic. But normally after the frontend logic some data is also changed, consequently, a new question arises: How can we transmit updated frontend values to the backend for processing in our ABAP event handler?

2.4 Receiving Data in the Backend (ABAP) Sent from the Frontend


The simplest method is to use data binding, a common practice in UI5 development for monitoring value changes. For instance, this is useful when a user modifies an input field at the UI and you need to track this change in JavaScript. Therefore, instead of directly entering the value into the XML view, we utilize data binding now and adjust the view as follows:
<mvc:View controllerName="project1.controller.View1"
xmlns:mvc="sap.ui.core.mvc" displayBlock="true"
xmlns="sap.m" xmlns:z2ui5="project1.control">
<Page id="page" title="{i18n>title}">
<content>
<z2ui5:MyCustomControl id="test" value="{MY_BINDED_VALUE}" change="onMyBackendChangeHandler" />
</content>
</Page>
</mvc:View>

And we adjust the controller (which again only simulates the ABAP backend and will be replaced later):
sap.ui.define("project1/control/MyCustomControl", [
"sap/ui/core/mvc/Controller"
],

function (Controller) {
"use strict";

return Controller.extend("project1.controller.View1", {
onInit: function () {

var data = { 'MY_BINDED_VALUE' : "backend value" };
var oModel = new sap.ui.model.json.JSONModel(data);
this.oView.setModel( oModel );

},
onMyBackendChangeHandler: function (oEvent){
var data = this.oView.getModel().getData();
sap.m.MessageToast.show( 'Frontend Event raised, and processed in the backend, value: ' + data.MY_BINDED_VALUE );
}
});
});

Next we modify this value at the frontend by updating the property. We'll achieve this by enhancing our setTimeout method to include the setValue method call:
			setTimeout((oControl) => {
oControl.setValue("frontend value");
oControl.fireChange();
}, 3000, this);

The outcome appears as follows: initially, the view is rendered using the value received from the backend:


CC - Initial Load (1/2)


And after 5 seconds, it is replaced with the frontend value set by the JavaScript logic:


CC - Setting data in the frontend and sent to the backend (2/2)


As you can see, we simply need to update the property using the command:
oControl.setValue("frontend value");

The SAP UI5 framework automatically manages the remaining processes, significantly reducing the complexity that we typically encounter when handling frontend-to-backend data exchange. Furthermore, we will later observe that the abap2UI5 framework also streamlines the transfer of data binding to the attributes of the ABAP classes implementing 'z2ui5_if_app,' requiring no further actions.

Now let's assume that some backend logic is processed in response to the updated frontend values and the event. The final step is to determine a way to trigger a specific function on the frontend from the backend, which we will do next.

2.5 Calling Frontend Functions (JS) from the Backend


To prepare for this, we first include in our Custom Control a new frontend function and property:
sap.ui.define("project1/control/MyCustomControl",[
"sap/ui/core/Control"
], (Control) => {
"use strict";

return Control.extend("project1.control.MyCustomControl", {
metadata: {

properties: {
value: { type: "string" },
checkCallMyFunction: { type : "boolean" }
},
events: {
change: {}
}
},

setValue(value) {
this.setProperty("value", value);
},
getValue() {
return this.getProperty("value");
},
setCheckCallMyFunction(checkCallMyFunction) {
if (checkCallMyFunction) {
this.myFrontendFunction();
}
this.setProperty("checkCallMyFunction", false);
},
init() {
},

myFrontendFunction(){
sap.m.MessageToast.show( 'myFrontendFunction called' );
},

onAfterRendering() {

setTimeout((oControl) => {
oControl.setValue("frontend value");
oControl.fireChange();
}, 3000, this);

},

renderer(oRM, oControl) {
oRM.openStart("div", oControl);
oRM.openEnd();
oRM.write(oControl.getValue("value"));
oRM.close("div");
}
});
});

To keep it simple, we just call a message toast, but of course, more complex logic can be implemented here. The key to calling this function from the backend is to use a boolean property that we transport as an attribute and can toggle in the backend to true. Then every time the backend sets it to true, the function is called at the frontend. The following changes to the XML are necessary:
<mvc:View controllerName="project1.controller.View1"
xmlns:mvc="sap.ui.core.mvc" displayBlock="true"
xmlns="sap.m" xmlns:z2ui5="project1.control">
<Page id="page" title="{i18n>title}">
<content>
<z2ui5:MyCustomControl id="test" value="{/MY_BINDED_VALUE}" change="onMyBackendChangeHandler" checkCallMyFunction="true"/>
</content>
</Page>
</mvc:View>

And now, when we test the application, we can observe that the function is being called:


CC - Processing JS function, triggered from the backend









Info: We now have established all the essential communication mechanisms between the backend and frontend. Both sides are capable of exchanging data and triggering each other's functions.

2.6 Optional I: Implementing Faceless Controls

The example above includes the rendering of a UI. However, if your goal is solely to call functions at the frontend or integrate your JavaScript logic, you can opt to skip the rendering part and use it as a 'faceless' Custom Control. In this scenario, only properties are transferred through the XML view, the rendere method is empty and there is no impact on the UI. For example take a look to the CC to set the title here or to read frontend information here.

2.7 Optional II: Loading External Libraries

More functionality can be included by using external libraries. You can also load these libraries in your custom controls, as demonstrated in the bwip-js custom control example. Check it out here.

3. Backend Development (ABAP)


Now, we want to transfer the Custom Control functionality to abap2UI5. If you're primarily a UI5 developer, this would be the perfect moment to reach out to your ABAP colleagues for assistance... 🦖🦖🦖

3.1 Migrating Frontend Logic to the ABAP Backend


Access your ABAP backend system and create a new ABAP class with a new method named 'get_js' and a single return parameter. Next, ensure that the following option is activated in Eclipse:


Eclipse - Setting to copy & paste large string from the clipboard


Now, we copy & paste the entire Source Code of the Custom Control into Eclipse:


press strg+v now!


The snippet of the ABAP class now looks as follows:


Javascript of the CC after copy & paste to ABAP


Next, we replace the namespace for its use in abap2UI5. In our case, the original namespace was 'project1.control'. We will substitute this with 'z2ui5':


CC with replaced Namespaces and with added sap.declare function at the beginning


Check out the full source code on Github:


Source Code (here)



3.2. Developing an abap2UI5 Application Using the New Custom Control


As mentioned earlier, the controller and XML View that we initially developed in JavaScript will now be implemented in ABAP. Therefore, we are now creating an abap2UI5 app. For this, we create another class and implement the interface 'z2ui5_if_app'. In the abap2UI5 app, the XML View with the new CC from above now appears as follows:
    DATA(view) = z2ui5_cl_xml_view=>factory( ).
client->view_display( view->shell(
)->page( 'abap2UI5 - My CC App'
)->_generic(
name = `MyCustomControl`
ns = `z2ui5`
t_prop = VALUE #(
( n = `value` v = client->_bind_edit( mv_binded_value ) )
( n = `change` v = client->_event( 'MY_FRONTEND_EVENT' ) )
( n = `checkCallMyFunction` v = `true` )
)
)->stringify( ) ).

And the logic for the controller in JavaScript now looks in ABAP in the abap2UI5 app as follows:
    CASE client->get( )-event.
WHEN 'ON_CC_LOADED'.
display_view( ).
WHEN 'MY_FRONTEND_EVENT'.
client->message_box_display( |Frontend Event raised, and processed in the backend, value: { mv_binded_value }| ).
WHEN 'BACK'.
client->nav_app_leave( client->get_app( client->get( )-s_draft-id_prev_app_stack ) ).
ENDCASE.

Additionally, we need to implement some logic to ensure that the Custom Control is loaded first upon the application's start before we can utilize it in our view. We will set up this process as follows:
    DATA(view) = z2ui5_cl_xml_view=>factory( ).
client->view_display(
view->_generic( ns = `html` name = `script` )->_cc_plain_xml( zcl_a2ui5_my_custom_control=>get_js( )
)->_z2ui5( )->timer( client->_event( `ON_CC_LOADED` )
)->stringify( ) ).

Check out the full source code on GitHub:


Source Code of the abap2UI5 Demo App (here)


Now, you can start the new app. The entire logic that was previously processed as a UI5 frontend application is now integrated into the backend and can be utilized through abap2UI5 apps. The final demo, running in abap2UI5, now appears as follows:


abap2UI5 app - Demo using the new CC


This concludes our development journey, which began at the frontend, progressed into the backend, and finally ends in the use within an abap2UI5 app. The Custom Control till now just contains basic functions, you can now begin to enhance it with more JavaScript logic.

4. Additional Comments


There are multiple ways to develop and encapsulate JavaScript logic and integrate it into abap2UI5. The method explained here is simply the one that has worked best so far. The ease of using data binding and events on the frontend greatly simplifies interaction with the backend. And in the backend, the Custom Control ensures seamless use in ABAP via properties in Views in abap2UI5 apps.

However, improvements are always possible. If you prefer other methods, feel free to use what works best for you. As highlighted in part 5 of the series, you can send plain JavaScript, CSS, and HTML to the frontend. Essentially, you have complete freedom in your approach and you might find more suitable methods to meet your specific needs.

Furthermore, this was just a basic example. Custom Controls can be developed to be much more complex, for example with features like aggregations. Be sure to also explore more blog posts about Custom Control development and experiment for yourself.

The full source code is available on GitHub, check it out here. It's open for future extensions, and you're welcome to create issues and PRs.

5. Conclusion


This concludes our guideline on how to enhance abap2UI5 with new JavaScript logic.

Thank you for reading, and I hope this inspires you to experiment with adding your own functionalities to abap2UI5. 👷‍♀️👷

Your feedback is always appreciated. Feel free to raise an issue or leave a comment.
Labels in this area