Skip to Content

Since the introduction of HTML Islands in Web Dynpro for ABAP (WDA) it’s possible to integrate more or less any web-based content. And as FPM is based on WDA, this is valid also for freestyle UIBBs in FPM (see for example the blog

How to use a Java Script Chart Library in Floorplan Manager Applications).This is also valid for UI5 based content. In this blog I want to demonstrate how a UI5 control can be integrated into FPM. I chose the timeline control as example, as WDA doesn’t offer such a control. By following this example, you should be able to integrate any other UI5 control as well.

To do this we will:

  1. Create a Freestyle UIBB showing an HTML Island
  2. Add client-side JavaScript code to create and embed the  timeline control in the HTML Island
  3. Expose timeline data from the backend to the client and feed it into the timeline control
  4. Trigger a Web Dynpro Action from the timelines “select” event and inform the backend about the selected item

To understand this blog some basic knowledge in Web Dynpro for ABAP programming and FPM configuration is required. It’s a very detailed step-by-step description how to create this freestyle UIBB. I will also explain the necessary JavaScript coding, so that you don’t need JavaScript knowledge.

This blog is based on functionality available with Netweaver 7.40.

Create a Freestyle UIBB

First we need to create our new UIBB. To do this we go to transaction SE80 and create a new Web Dynpro component. The name is arbitrary – I will use ZSCN_TIMELINE_UIBB and use W_TIMELINE as window and V_TIMELINE as View name. I will also use an assistance class named ZCL_TIMELINE_ASSIST and Implement the UIBB interface IF_FPM_UI_BUILDING_BLOCK on this component

create component.png

In the end the component structure should look like this:

component structure.png

To embed and interact the UI5 control we need to add an HTML island to view V_TIMELINE and assign “<div id=”mytimeline” style=”height:100%”></div>” as value for property “staticHtml”.

To make the UI5 timeline control available to this HTML Island we have to add the necessary script accesses: To do this

  • we add a Script Element to the HTML Island and add as source to path to the UI5 core library (which is “/sap/public/bc/ui5_ui5/1/resources/sap-ui-core.js”).
  • Then we need to add 2 custom attributes to the Script Element:
    • One with the name “id” and value “sap-ui-bootstrap” and
    • a 2nd with name “data-sap-ui-libs” and value “sap.ui.commons,sap.suite.ui.commons”.

This will make sure that UI5 and the needed libraries are loaded at runtime.

Your view should now look like this:

initial view.png

Don’t forget to save and activate everything you made so far!

Create a Test Application

Although our UIBB isn’t capable of displaying anything so far, it’s now time to create a test application where we can see and test the progress of our work.

  • Call Transaction FPM_WB. It will launch the FPM Workbench in a new browser window. There you can find the link to launch the “Wizard for Creating Empty FPM Applications”. Click on it and you will get to the initial screen of the Application Creation Tool
  • Fill it out as shown in the screenshot

ACT.png

  • then press “Next” and on the next screen “Save” which brings you to the confirmation screen of the application creation tool. Please keep this screen open until the end of this chapter
  • The confirmation screen contains mainly 2 links, one to launch the configuration editor (“FLUID”) and the 2nd to launch the new application. As the application is up to now completely empty, we first launch the configuration editor
  • There add our new UIBB to the application and give the only page as well as the panel a meaningful title (see screenshot)

FLUID.png

  • After saving we can start the new application via the 2nd link on the confirmation screen.

As we did not add any content (except an empty DIV tag) to our Html Island there is only an empty space in the Timeline panel.

Add the Timeline Control with JavaScript

Now we have to add the necessary client side code to create the timeline control and place it into our Html Island. To do this create a simple text file, add the following code to it and save it (best with extension “.js”).


var MY_TIMELINE = MY_TIMELINE || {
      timeline: {},
      oCallbackApi: {},
      init: function (oCallbackApi) {
          this.oCallbackApi = oCallbackApi;
          this.timeline = new sap.suite.ui.commons.Timeline();
          this.timeline.placeAt("mytimeline");
     }
}






























Let me shortly explain this code: We are creating one instance of object MY_TIMELINE on the client. This object has 3 members:

  • an empty object timeline, which we will use to store the timeline control,
  • an empty object oCallbackApi, which will hold the reference to a callback object, which we will use to communicate to the backend
  • and an init function. With this init function we are
    • receiving the callback object and storing it
    • create the Timeline control and
    • place it in our Html Island

Now we have to upload this code as MIME object to our new component. Launch SE80 and navigate to our UIBB component. Import your javascript file as Mime object via context menu on the component.

import mime.png

Now add this script as additional script to the Html Island

view with script.png

At runtime the script will be loaded and the Html Island can access the script. However to create and locate the timeline control the init method has to be called. Therefore we need to add a call to this method to our Html Island and this is done by adding the following ABAP code to the view’s WDDOMODIFYVIEW method:


METHOD wddomodifyview .
   IF first_time = abap_true.
     DATA(lo_timeline) = CAST cl_wd_html_island( view->get_element( `TIMELINE` ) ).
     lo_timeline->add_script_call( cl_wd_html_script_call=>new_call(
         )->variable( `MY_TIMELINE`
         )->function( `init`
         )->add_callback_api(  ) ).
ENDIF.
ENDMETHOD.


























We simply add a script call object to our Html Island. The script call is calling the init function on (object) variable MY_TIMELINE and is adding a single parameter to the script call – the call back API. This JavaScript function will now be called on the client. If you run the test application you can see only a small change on the UI, a filter icon. However this filter icon belongs to the UI5 timeline control – we have now successfully instantiated the UI5 control and embedded it into our FPM application.

test application I.png

Feed the control with application data from the backend

A timeline control without any data isn’t very exciting, therefore let it display some backend data. First let’s create some timeline data. We will do this in our assistance class:

  • Create 2 public types for storing the data:

types: begin of TY_S_ITEM,
            datetime type string,
            user_name type string,
            title type string,
            text type string,
            icon type string,
          end of ty_s_item.
   types: ty_t_item type STANDARD TABLE OF ty_s_item with DEFAULT KEY.






















  • Then add a public method Get_Timeline_Data to your assistance class

methods GET_TIMELINE_DATA
     returning
       value(RT_DATA) type TY_T_ITEM .























  • And add some code creating some sample data to the methods body (I chose to use some version control data for this example).

DATA(lo_version_provider) = cl_wb_object_version_provider=>create_instance( ).
       DATA(lo_version_info) = lo_version_provider->get_versions(
                              pgmid                   = 'R3TR'
                              object_type             = 'CLAS'
                              object_name             = 'CL_FPM_OVP_ASSIST'
                          ).
       LOOP AT lo_version_info->get_list( ) ASSIGNING FIELD-SYMBOL(<version_list>).
         DATA(ls_vers_info) = <version_list>->get_info( ).
         APPEND INITIAL LINE TO rt_data ASSIGNING FIELD-SYMBOL(<item>).
         CONVERT DATE ls_vers_info-datum TIME ls_vers_info-zeit
                 INTO TIME STAMP DATA(ts) TIME ZONE sy-zonlo.
         <item>-datetime = |{ ts TIMESTAMP = ISO TIMEZONE = 'UTC   ' }|.
         <item>-user_name = ls_vers_info-author.
         <item>-title = ls_vers_info-korrnum.
         <item>-text = ls_vers_info-trequest_text.
         <item>-icon = 'sap-icon://request'.
       ENDLOOP.


























  • Now we have to create a context node in the view to store the data and transport it to the UI. We will create a 0..n node with the structure defined by TY_S_ITEM

view context.png

  • And in the view’s WDDOINIT method we will fill this node:

method WDDOINIT .
   data(lo_nd_item) = wd_context->get_child_node( name = wd_this->wdctx_item ).
   lo_nd_item->bind_table( new_items = wd_assist->get_timeline_data( ) ).
endmethod.























  • The data is now accessible in the view contoller’s context. Now we will bind it to the Html Island. To do this we have to first create the binding targets on the Html Island. Therefore we add a JsonDataSource and JsonDataElements corresponding to our context structure to the Html Island and bind our context to them

view with Data elements.png

Pay attention to the name attributes. These are used to access the data in the JavaScript code. I chose “content”, “datetime”, “userName”, “title”, “text” and “icon”. If you are using other names you have to adjust the script accordingly.

  • Now the timeline data is available on the client and we have to fill our timeline control with the data. Therefore we will now extend our script:

var MY_TIMELINE = MY_TIMELINE || {
    timeline: {},
    oCallbackApi: {},
  init: function (oCallbackApi) {
       this.oCallbackApi = oCallbackApi;
       this.timeline = new sap.suite.ui.commons.Timeline();
       this.timeline.placeAt("mytimeline");
   },
    setContent: function (content) {
       this.timeline.destroyContent();
       for(var i=0, max=content.length; i<max; i++) {
            var item = new sap.suite.ui.commons.TimelineItem({
                 dateTime : new Date(content[i].datetime),
                 userName : content[i].userName,
                 title : content[i].title,
                 text : content[i].text,
                 icon : content[i].icon
            });
            this.timeline.addContent(item);
       }
  }
}





















To pass the data to the control I added the setContent function to the script. This method will get the timeline data as an array parameter and it will create timeline items for each array entry.

  • Re-upload the changed JavaScript file to your component
  • Last step call the new JavaScript function from the backend: Add the following script call to the WDDOMODIFYVIEW method of V_TIMELINE

    lo_timeline->add_script_call( cl_wd_html_script_call=>new_call(
         )->variable( `MY_TIMELINE`
         )->function( `setContent`
         )->add_ui_elem_data_source( `content` ) ).



















Now everything should be done and the test application should show some data (if it doesn’t work you have to clear the browser cache):

test application II.png

Sending the Selected Item to the Backend

Up to now, we have embedded a UI5 control in to an FPM application and filled it with data from the ABAP backend. What’s missing is the other direction: Sending data from the control to the backend.

This is done by raising a Web Dynpro Action from the client-side JavaScript code.

  • Therefore we will start by adding a new action “SELECT” to view V_TIMELINE.
  • Let’s add some simple code to the action handler, which will later show that the SELECT action has been triggered:

method ONACTIONSELECT .
   cl_fpm_factory=>get_instance( )->mo_message_manager->report_message(
     EXPORTING
       iv_message_text  = wdevent->get_string( `DATA` )
   ).
endmethod.















  • Then we have to add an HtmlEvent to our Html Island and attach the newly created “SELECT” action to it

view with Html event.png

  • Now the backend is ready – the only thing missing is to raise the Html Event via JavaScript. To do this we have to enhance our timeline.js script once more:

var MY_TIMELINE = MY_TIMELINE || {
    timeline: {},
    oCallbackApi: {},
     init: function (oCallbackApi) {
          this.oCallbackApi = oCallbackApi;
          this.timeline = new sap.suite.ui.commons.Timeline();
          this.timeline.placeAt("mytimeline");
          this.timeline.attachSelect(
               function(oControlEvent) {
                    MY_TIMELINE.oCallbackApi.fireEvent('select', oControlEvent.getParameter("selectedItem").getTitle());
               }
          );
     },
     setContent: function (content) {
          this.timeline.destroyContent();
          for(var i=0, max=content.length; i<max; i++) {
               var item = new sap.suite.ui.commons.TimelineItem({
               dateTime : new Date(content[i].datetime),
               userName : content[i].userName,
               title : content[i].title,
               text : content[i].text,
               icon : content[i].icon
               });
               this.timeline.addContent(item);
          }
     }
}












The only changes are lines 8-12: There we attach a new function to the control’s Select event. In this function we are using the callback api we already stored in the first version of the script to forward this client-side event to the backend, where it then raises the SELECT-Action of our view. As event parameter we pass the selected items title to the backend.

Now if run the test application again and click on any item we should get a (error) message with the item’s title as text:

test application III.png

Summary

Although this blog is quite lengthy, it’s not much effort to integrate a UI5 control. The integration control is the Html Island. Via standard WD Abap methods like context binding the application data is passed to the Html Island. The rendering, and placing of the control as well as the data transport from the Html Island to the control is then done via JavaScript coding attached to the Html Island. The backward direction is done by raising HtmlEvents via JavaScript coding, which then triggers Web Dynpro Actions.

My step-by-step description was reduced to the absolute minimum necessary to demonstrate the integration of a UI5 control. Therefore the result is a bit ugly and there is still a lot of work to be done before such a UIBB can be used productively. But the basic steps are the same as the FPM team used when implementing the Carousel or the Visbiz UIBB.

To report this post you need to login first.

8 Comments

You must be Logged on to comment or reply to a post.

  1. Edgar Meyer

    Hi Christian,

    I tried this according to your description but I got no display.

    Only the area in the OVP and the text is displayed.

    Could you help?

    Thanks, Edgar/wp-content/uploads/2015/07/timeline_749952.jpg

    (0) 
  2. Christian Guenther

    Hello Edgar,

    there’s of course a lot of reasons what might be wrong, if the UI5 control simply doesn’t show up. To analyze the issue I would

    1. Clear the browser cache and retry
    2. Call the technical help (via context menu) and check whether my HTML island is there, whether the staticHTML is correct, whether I gave it a fixed height and width and whether my timeline script is there as welltechnical help.png
    3. I would make sure that my script is uploaded to my component and that the name matches (no typos)
    4. I would by using the ABAP debugger make sure that in the WDDOMODIFYVIEW method of the Html Island’s view the init method is called
    5. I would go to the browser’s developer tools and check if the timeline.js script is loaded, if there are javascript errors and finally debug the timeline.js

    If you are familiar with javascript and the browser’s developer tools I would start with 5.

    Hope this helps!

    Best regards,

      Christian

    (0) 
  3. Robbe Wuyts

    Hi Christian,

    Excellent blog!

    I tried the same with a HTML container and the UI5 CK Editor, but there is a problem: when I put the written text in a context attribute, the CKeditor control disappears… Maybe you can help me?

    editor.js

    jQuery.sap.registerModulePath(“openui5″,”/sap/bc/webdynpro/sap/zz_test_ckeditor”);

    jQuery.sap.require(“openui5.ckeditor”);

    jQuery.sap.require(“openui5.CKEditorToolbar”);

    var MY_EDITOR = MY_EDITOR || {

        editor: {},

        oCallbackApi: {},

        init: function (oCallbackApi) {

              this.oCallbackApi = oCallbackApi;

              this.editor = new openui5.CKEditor({

                uiColor: ‘#0090ff’

              });

              this.editor.placeAt(“myeditor”);

         },

        getHTML: function(){

            MY_EDITOR.oCallbackApi.fireEvent(‘save’, this.editor.getValue());

        },

        setHTML: function(text){

            this.editor.setValue(text);

        }

    }

    wddomodifyview

    METHOD wddomodifyview .
       IF first_time = abap_true.
         wd_this->editor = CAST cl_wd_html_container( view->get_element( ‘CK_CONTAINER’ ) ).
         wd_this->editor->add_script_call( cl_wd_html_script_call=>new_call(
             )->variable( ‘MY_EDITOR’
             )->function( ‘init’
             )->add_callback_api( ) ).
       ENDIF.
    ENDMETHOD.

    onactionfiresave

    METHOD onactionfiresave .
       wd_this->editor->add_script_call(
         cl_wd_html_script_call=>new_call(
                )->variable( ‘MY_EDITOR’
                )->function( ‘getHTML’
                ) ).
    ENDMETHOD.

    onactionsave

    METHOD onactionsave.
       wd_context->set_attribute(
         EXPORTING
           name  = ‘text’
           value = wdevent->get_string( `DATA` )
       ).
    ENDMETHOD.

    structure:

    before save:

    after save:

    Maybe you can help me?

    Kind regards,

    R. Wuyts

    (0) 
    1. Christian Guenther

      Hello Robbe,

      basically everything is working fine in your example, only after pressing save the editor control isn’t rendered anymore. I suppose that this happens because the view get’s rerendered, and therefore the HTML container get’s reinstantiated (and the editor control is gone).

      You should make sure that the editor control isn’t rerendered. To achieve this you should put the 2 panels on different views and embed these views in your current view. Delta rendering is based on view level, so when something in the view changes the complete view has to be rerendered.

      As you change the text in panel 2 your whole view has to be rerendered – and this kills your control. If panel 2 is embedded in a view and the control in another, there is no need to rerender the control’s view on changing the context of the lower panel.

      You can check quite easy if that’s the cause if you skip the code in onactionsave. If you don’t update the context there the control should remain.

      Hope this helps.

      Best regards,

      Christian

      (0) 
      1. Robbe Wuyts

        Hi Christian,

        I placed the 2 panels into different views and embedded those two views in my main view. Somehow the ckeditor still dissapears when I press the button.

        I also skipped the code in onactionsave method, still the same result…

        Do you have any idea what else could be wrong?

        Btw, I checked in chrome developer tools, and there is no any error listed in the console.

        Kind regards,

        Robbe Wuyts

        (0) 
        1. Christian Guenther

          Hello Robbe,

          then it’s probably the HTML Container, whcih causes the trouble. Can you try replacing it with an HTML Island – the Island is probably more stable against rerendering than the HTML container.

          Best regards,

            Christian

          (0) 
          1. Robbe Wuyts

            Hi Christian,

            Indeed, I tried the same with the HTML Island this morning, and now it works fine. The only thing I’m struggling with is the height and width of a HTML Island: seems like I can’t use ‘%’?

            Kind regards,

            Robbe

            (0) 
            1. Christian Guenther

              Hello Robbe,

              you can use ‘%’ and indeed to make it resizeable (so that the control adjusts itself to the available space) you have to work with % values (normally it’s always 100%).

              If on using percentage values the HTML Island isn’t visible anymore then the reason is that 100% are now 100% of 0px => You have to make sure that the relative height (or width) is relative to something with a height  > 0px – therefore you have to ewnsure that the environment allows relative sizes:

              • First you have to make sure that the other elements on the page leave empty vertical/horizontal space – if the page already is longer than the available vertical space any percentage value is “% of 0”
              • To ensure that FPM renders all surrounding containers the right way please make sure that your UIBB is embedded in the floorplan with the attribute “Stretching” set to “UIBB expands with available space”
              • Within your UIBB you should make sure that all containers where your HTML Island is directly or indirectly is embeeded are using up all available space as well (best is to use Matrix-Layouts and set the widths and heights of them to 100% and as well set the STRECHED_VERTICALLY and STRECHED_HORIZONTALLY Flags of the layout.
              • If the HTML Island is still not visible you can try to start the application with additional URL parameter FPM_SCROLLING_MODE=02. This will remove the inner scroll container and activate the browser’s scroll container (if you have a resizeable control in your page inner scrollbars are useless anyway)

              Best regards,

                Christian

              (0) 

Leave a Reply