Technology Blogs by SAP
Learn how to extend and personalize SAP applications. Follow the SAP technology blog for insights into SAP BTP, ABAP, SAP Analytics Cloud, SAP HANA, and more.
cancel
Showing results for 
Search instead for 
Did you mean: 
karin_voss
Member

Introduction


As application developer you want to enhance your Web Dynpro ABAP application also with controls from other sources, if this control is not offered by Web Dynpro ABAP.

Your first choice should be a UI5 control:

The control libraries of Web Dynpro ABAP and UI5 support the same themes and product standards as accessibility (ARIA) or security (CSP), therefore the UI will look nice and seamless and can be used consistently. In addition, the UI5 library is readily available in the ABAP server.

Technically the Web Dynpro ABAP framework offers two UI elements which can embed an UI5 control:

  1. HTML Island

  2. IFrame


Until SAP_UI 7.54 the only embedding option was via HTML Island. Starting with SAP_UI 7.55 the IFrame UI element in Web Dynpro ABAP allows the communication via PostMessage and enables the embedding of UI5 controls into the IFrame UI element.

This blog highlights an example for this new implementation option.

Sample application WDR_TEST_IFRAME_UI5


The sample application WDR_TEST_IFRAME_UI5 is available from SAP_UI 7.57 onwards.


Sample application WDR_TEST_IFRAME_UI5


The sample application embeds the UI5 ColorPicker control into a WebDynpro IFrame.
This very simple sample application works as follows:

  • Starting the application initializes the UI5 ColorPicker

  • The initial displayed color is set programmatically to “#ff00ff” by WebDynpro. It is shown by the ColorPicker as well as by the WebDynpro InputField at the top.

  • Clicking on the ColorPicker passes the newly selected color via PostMessage to WebDynpro and it is displayed in the WebDynpro InputField.

  • Entering a new color into the InputField and pressing the <enter> key or selecting then button "Set Color" sends a PostMessage to the IFrame passing the new color. The ColorPicker gets updated accordingly.


Implementation


There are 3 parts to be developed:

  1. WebDynpro Part
    WebDynpro component containing the IFrame that shows the UI5 control and the methods to communicate with the IFrame

  2. HTML Part
    File containing the required UI5 sources and pointing to the JavaScript source

  3. JavaScript Part
    File containing the JavaScript source to instantiate the UI5 ColorPicker and to communicate with the IFrame


The implementation details of each part are described below using the sample application WDR_TEST_IFRAME_UI.



WebDynpro Part


The WebDynpro part defines the WebDynpro component with a view containing an IFrame UI element with the HTML file defined as source.


Sample WebDynpro component WDR_TEST_IFRAME_UI5




  • The HTML file and the JavaScript file are stored in the MIME repository as part of the Web Dynpro component.

  • Property lifeTime
    In this example the default value "whenVisible" is kept. In this case the communication via PostMessage is only possible when the ColorPicker is visible on the screen.
    You should define lifeTime = "whenAlive", if you need to communicate with the UI5 control also when it is not visible, but already instantiated. For example, the UI5 control is embedded into a TabStrip control. Once the tab is selected, the UI5 control gets instantiated. If the user selects a different tab, the UI5 control is not visible anymore, but with lifeTime = "whenAlive" still alive.

  • Property source
    In this example, the source is bound to the context. In WDDOINIT the current theme is retrieved and concatenated as URL parameter "?sap-theme=current_theme_id" with MIME repository path of the HTML file. That way, the ColorPicker is rendered according to the current theme.
    The source value in this example is:
    /sap/bc/webdynpro/sap/wdr_test_iframe_ui5/~wdr_test_iframe_ui5/wdr_test_ui5_colorpicker.htm?sap-theme=_current_theme_

  • Event onPostMessage
    Register on the event onPostMessage to be able to communicate with the IFrame.


IFrame - Communication via PostMessage


You have the possibility to send messages to an IFrame being included in a WebDynpro component by using public method POST_MESSAGE of the Web Dynpro ABAP class CL_WDR_IFRAME_POST_MESSAGE. Using this you have to hand over the IFrame object reference and the Message to be send via the respective importing parameters.

The IFrame itself can as well send messages via the HTML postMessage functionality. This message can be caught by the embedding Web Dynpro component by binding an action handler to the IFrame event onPostMessage.

The Message parameter is of type string. A common format of the message string is JSON which is also used in this sample application. It is important to define unique Message strings so that only your function will execute an action.
In this sample the JSON string contains an action name with the prefix “wdr_test” to have unique strings.

In this sample in the following situations event onPostMessage is raised from the IFrame.

  • UI5 ColorPicker is initialized. Message passed is
    {"action":"wdr_test_ui5_initialized"}

  • Enduser clicks on ColorPicker to change the color. Message passed is
    {"action":"wdr_test_ui5_color_changed","color":"#ce4bce"}


In this sample in the following situations CL_WDR_IFRAME_POST_MESSAGE=>POST_MESSAGE is called in WebDynpro:

  • Pass initial color to ColorPicker after it was initialized. Message passed is
    {"action":"wdr_test_initialize_color_picker", "color":"#ff00ff"}

  • Enduser entered new color in InputField. Message passed is
    {"action":"wdr_test_set_color", "color":"#ff0000"}


Method for action handler of onPostMessage Event from IFrame:
method onactionpost_message .

"onPostMessage event handler of IFrame
if id ne `IFRAME_COLORPICKER`.
return.
endif.

"parse post message string from IFrame:
"common format of the message string is JSON
data(lv_message) = replace( val = message regex = `[:]|[,]|["]|[{]|[}]` with = ` ` occ = 0 ).
split condense( lv_message ) at ` `
into: data(lc_action) data(lv_action) data(lc_color) data(lv_color).

case lv_action.
when `wdr_test_ui5_initialized`.
"UI5 window is initialized, it is now possible to send a PostMessage to the IFrame
wd_this->send_post_message_to_iframe( action = `wdr_test_initialize_color_picker` ).

when `wdr_test_ui5_color_changed`.
"end user clicked on ColorPicker to change the color -> update InputField with current color
data(lo_nd_color) = wd_context->get_child_node( name = wd_this->wdctx_color ).
data(lo_el_color) = lo_nd_color->get_element( ).

lo_el_color->set_attribute( name = `SELECTED_COLOR` value = lv_color ).

endcase.

endmethod.

 

Method to send PostMessages from WebDynpro to IFrame:
method send_post_message_to_iframe .

"get current color from context (color displayed in InputField)
data(lo_nd_color) = wd_context->get_child_node( name = wd_this->wdctx_color ).
data(lo_el_color) = lo_nd_color->get_element( ).
data lv_color type wd_this->element_color-selected_color.

lo_nd_color->get_attribute(
exporting
name = `SELECTED_COLOR`
importing
value = lv_color ).

" create json string
data lv_post_message type string.
lv_post_message = |\{"action":"{ action }", "color":"{ lv_color }"\}|.

"call IFrame post message and pass json string
cl_wd_iframe_post_message=>post_message(
exporting
ir_iframe_view_element = wd_this->mo_color_picker
iv_message = lv_post_message ).

endmethod.

 

HTML Part


The HTML part is a html file which is saved in the MIME repository of the WebDynpro component.

It contains the location of the UI5 source, the UI5 libs to be loaded, and the location of the JavaScript source.

Sample html file "wdr_test_ui5_colorpicker.htm"
<!DOCTYPE html>
<html>

<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta charset="UTF-8">

<script type="text/javascript"
src="/sap/public/bc/ui5_ui5/resources/sap-ui-core.js"
data-sap-ui-libs="sap.m, sap.ui.core, sap.ui.layout, sap.ui.unified"
data-sap-ui-async="true"
data-sap-ui-resourceroots='{"sap/wdr_test_iframe_ui5": "./"}'
data-sap-ui-oninit="module:sap/wdr_test_iframe_ui5/wdr_test_ui5_colorpicker">
</script>
</head>

<body class='sapUiBody' id="content"></body>
</html>


 

Explanations for the above html file:

  • To be CSP compliant, don't use directly executable code in your HTML file.


  • src="/sap/public/bc/ui5_ui5/resources/sap-ui-core.js"​

    Sets the UI5 resource, which is stored in the MIME repository.


  • data-sap-ui-libs="sap.m, sap.ui.core, sap.ui.layout, sap.ui.unified"

    Sets the UI5 libs to be loaded. This is dependent on the UI5 control you are going to embed.
    You can double check via <F12> developer tools of the browser, which libraries are loaded and add them accordingly. The list should be complete to avoid reloading required libs synchronously.


  • data-sap-ui-async="true"​

    The UI libs must be loaded asynchronously. This is a given requirement for CSP compliance.
    It loads the required files in parallel and significantly improves performance of the loading processes.


  • data-sap-ui-resourceroots='{"sap/wdr_test_iframe_ui5": "./"}'​

    In the data-sap-ui-resourceroots attribute you need to define an URL for the namespace, from which to load the code. In this case, the URL "./" is relative, since in the coding sample the HTML and the JavaScript source are located next to each other, but the URL can also be specified absolutely. In general, the URL will be evaluated relative to the document.base.URI.
    The namespace, here "sap/wdr_test_iframe_ui5", is in principle freely selectable. However, the namespace should be globally unique to avoid name conflicts if code from other projects is loaded. In the sample the name of the WD component was reused as namespace.


  • data-sap-ui-oninit="module:sap/wdr_test_iframe_ui5/wdr_test_ui5_colorpicker">​

    The value of the data-sap-ui-oninit attribute contains the name of the module to be started in the JavaScript file. The name of the module starts with the namespace "sap/wdr_test_iframe_ui5".


  • <body class='sapUiBody' id="content"></body>​

    The id "content" is referenced later in the JavaScript part to place the ColorPicker: oColorPicker.placeAt("content");
    Check if class='sapUiBody' is required. It will also render the theme dependent background color. It can be omitted.

  • Do not set Styles in .html file: if it is required, then only via CSS Styles.


 

JavaScript Part


Contains the module defined in the HTML part tag data-sap-ui-oninit.
It is instantiating the UI5 control, receiving and sending PostMessages from and to the IFrame.

Sample JavaScript source "wdr_test_ui5_colorpicker.js "
sap.ui.define([
"sap/ui/unified/library",
"sap/ui/unified/ColorPicker"
], function(unifiedLibrary, ColorPicker) {
"use strict";

var ColorPickerMode = unifiedLibrary.ColorPickerMode;
var ColorPickerDisplayMode = unifiedLibrary.ColorPickerDisplayMode;

// ColorPicker instance
var oColorPicker;

// color passed from WebDynpro when ColorPicker not yet initialized
var sRememberedColor;

// helper functions
function initialize(color) {
// initialize ColorPicker
oColorPicker = new ColorPicker({
mode: ColorPickerMode.HSL,
displayMode: ColorPickerDisplayMode.Simplified,
change: handleChange,
});
// set color
if (sRememberedColor && oColorPicker.isColor(sRememberedColor) === true){
oColorPicker.setColorString(sRememberedColor);
sRememberedColor = "";
}
else if (oColorPicker.isColor(color) === true){
oColorPicker.setColorString(color);
}

// render
oColorPicker.placeAt("content");
};

// pass changed color via post message to WebDynpro
function handleChange(oEvent) {
const objPostMessage = {action: "wdr_test_ui5_color_changed", color: oEvent.getParameters().hex};
parent.postMessage(JSON.stringify(objPostMessage), window.location.origin);
};

// post message event listener
window.addEventListener("message", processMessage, false);
function processMessage(event) {
if (event.origin !== window.location.origin) {
return;
}
var oMessage;
try {
oMessage = JSON.parse(event.data);
}
catch(err) {
return;
}

switch (oMessage.action) {
case "wdr_test_initialize_color_picker":
// initialize ColorPicker
var color = oMessage.color;
initialize(color);
break;

case "wdr_test_set_color":
// update ColorPicker with color value from WebDynpro
// check if ColorPicker is available - due to asynchronous loading there is a small chance
// that the ColorPicker has not yet been instantiated
// therefore keep color and use later for initialze
if (typeof oColorPicker === 'object') {
if (oColorPicker.isColor(oMessage.color) === true) {
oColorPicker.setColorString(oMessage.color);
}
}
else {
sRememberedColor = oMessage.color;
}
break;
default:
//undefined action
break;
}
}

// after initial load of IFRAME send postMessage to WebDynpro
const objPostMessage = {action: "wdr_test_ui5_initialized"};
window.parent.postMessage(JSON.stringify(objPostMessage), window.location.origin);
});

 

Explanations for the above JavaScript source:
sap.ui.define([
"sap/ui/unified/library",
"sap/ui/unified/ColorPicker"
], function(unifiedLibrary, ColorPicker) {
"use strict";
...
});


  • "sap_ui.define" defines a JavaScript module. The typical and only suggested usage of this method is to have one single, top-level call to one JavaScript resource (file).
    The module ID was omitted, it is substituted by the ID that was used to request the module.

  • In the dependencies array (marked in green) each entry represents the name of another module that the currently defined module depends on. Each dependency module will be provided as a parameter, the order of the parameters will match the order of the modules in the dependencies array.

  • The "use strict"; literal expression was introduced by JavaScript 1.8.5 (ECMAScript 5). It tells the browser to execute the code in a so called “strict mode”. The strict mode helps to detect potential coding issues at an early state at development time, that means, for example, it makes sure that variables are declared before they are used. Thus, it helps to prevent common JavaScript pitfalls and it’s therefore a good practice to use strict mode.


...
var ColorPickerMode = unifiedLibrary.ColorPickerMode;
var ColorPickerDisplayMode = unifiedLibrary.ColorPickerDisplayMode;
...
// initialize ColorPicker
oColorPicker = new ColorPicker({
mode: ColorPickerMode.HSL,
displayMode: ColorPickerDisplayMode.Simplified,
change: handleChange,
});
...


  • Local shortcut names "ColorPickerMode" and "ColorPickerDisplayMode" were assigned to the enums from the sap.ui.unified library. This is best practice in UI5.


...
// ColorPicker instance
var oColorPicker;

// color passed from WebDynpro when ColorPicker not yet initialized
var sRememberedColor;

// helper functions
function initialize(color) {
// initialize ColorPicker
oColorPicker = new ColorPicker({
mode: ColorPickerMode.HSL,
displayMode: ColorPickerDisplayMode.Simplified,
change: handleChange,
});
...
// render
oColorPicker.placeAt("content");
};

// pass changed color via post message to WebDynpro
function handleChange(oEvent) {
const objPostMessage = {action: "wdr_test_ui5_color_changed", color: oEvent.getParameters().hex};
parent.postMessage(JSON.stringify(objPostMessage), window.location.origin);
};

// post message event listener
window.addEventListener("message", processMessage, false);
function processMessage(event) {
if (event.origin !== window.location.origin) {
return;
}
...
}
...


  • By using sap.ui.define, defined variables and functions (marked in green) are local and do not conflict with global names.

  • Function processMessage will be executed when a PostMessage was sent from WebDynpro via CL_WD_IFRAME_POST_MESSAGE=>POST_MESSAGE()

  • With parent.postMessage, a PostMessage is sent to the WebDynpro IFrame

  • The event parameter MESSAGE of the PostMessage event is just a string. One common possibility is to use the JSON format for the MESSAGE string.

  • Important: Make PostMessage Parameters unique, e.g. via Prefix
    There could be also other software components that make use of the postMessage. Your code must be able to handle unexpected MESSAGE strings.
    In addition, you should define unique MESSAGE strings so that only your function executes an action. In this sample we have a JSON string like {"action":"wdr_test_set_color", "color":"#ff0000"}


// after initial load of IFRAME send postMessage to WebDynpro 
const objPostMessage = {action: "wdr_test_ui5_initialized"};
window.parent.postMessage(JSON.stringify(objPostMessage), window.location.origin);


  • Once the window is loaded, inform WebDynpro via PostMessage
    In the sample application, WebDynpro then sends the initial color to be displayed via PostMessage back to UI5.


Documentation Links



Conclusion


The blog provided all details on how to embed a UI5 control into a Web Dynpro ABAP IFrame UI element which allows to enhance your WebDynpro application with the large offering of UI5 controls.

As a result you get an application with additional functionality that has a consistent look and feel since the embedded UI5 controls support the same product standards and themes.

Feel free to share feedback or thoughts in a comment.

Also feel free to ask questions in the SAP Community: https://answers.sap.com/questions/ask.html?primaryTagId=462330605920974660730944876913277

 
1 Comment