Parallelization in Web UI – Part 2
Parallelization in Web UI
Part2
4. AJAX
Concept
AJAX (short for asynchronous JavaScript and XML) is a set of web development techniques utilizing many web technologies used on the client-side to create asynchronous Web applications. With AJAX, web applications can send data to and retrieve from a server asynchronously (in the background) without interfering with the display and behavior of the existing page. By decoupling the data interchange layer from the presentation layer, AJAX allows for web pages, and by extension web applications, to change content dynamically without the need to reload the entire page. Data can be retrieved using the XMLHttpRequest object.
AJAX is not a technology, but a group of technologies. HTML and CSS can be used in combination to mark up and style information. The DOM is accessed with JavaScript to dynamically display – and allow the user to interact with – the information presented. JavaScript and the XMLHttpRequestobject provide a method for exchanging data asynchronously between browser and server to avoid full page reloads.
For more information, please refer to the Wiki’ and W3Schools’ pages: https://en.wikipedia.org/wiki/Ajax_(programming) and http://www.w3schools.com/ajax/
AJAX in SAP CRM WebUI
As mentioned above some of the AJAX features are implemented in the standard CRM. One of the nice examples, where you can do a reverse engineering, is the Recent Items in CRM WebUI.
View: CRM_BSP_RECOBJ/RecentObjects
In this case, the AJAX call is implemented, right on the BSP page: RecentObjects.htm
<!–this code performs a callback to refresh the items list. Only executed the first time when the BOL objects are not loaded yet–> |
In this example, it is using relatively old asynchronous value fetch (AVF) functionality. However, in newer releases, after SAP Note: 2181937, the call looks like below:
thShortCutMgr.fetch(‘<%= controller->COMPONENT_ID %>’,’CL_CRM_BSP__RECENTOBJECT0_IMPL’,’display’); |
All the mentioned standard JS functions can be found in one of the JS files of THTMLB_SCRIPTS BSP application, e.g. scripts_areaframe.js, scripts_deltahandling_client.js. You can also search for the needed function right in the browser, e.g. in Chrome, activate Developer Tools (F12) à Sources à Menu à Search all Files (Ctrl + Shift + F).
How it works. A certain function (e.g. thAVFMgr.fetch, thShortCutMgr.fetch and many others) builds a proper AJAX URL, containing all necessary parameters. Then the function thtmlbAJAXCall.callBackend(AJAXUrl,CallBackEvent);is executed. The first parameter is our AJAX URL and the second is the call-back function that is registered to receive the result from the AJAX call.
Typical parameters that you need to provide to the back-end are the following: service handler, callback handler, DOM element id, sometimes type of the element.
Service Handler
Service handler, it’s the ICF service that you call on the backend. WebUI is using the standard service: /default_host/sap/webcuif/uif_callback, which has only one handler – CL_CRM_WEB_UTILITY.
Callback Handler
Callback handler, it’s the class that implements a callback method, which will be called in the AJAX call. This class should implement the interface IF_CRM_WEB_CALLBACK. And the mandatory callback method is IF_CRM_WEB_CALLBACK~HANDLE_REQUEST. For the JS object thAVFMgr, its callbackhandler is hardcoded in the JS file and depend on the callback JS function:
case “onRecentItemsRefresh”: if(!trigger) { return; } var ajaxURL = newSessionURL.URL + “/webcuif/uif_callback?crm_handler=CL_CRM_BSP__RECENTOBJECT0_IMPL“; ajaxURL += “&crm_controller=” + encodeURIComponent(trigger.id) + thtmlbBuildRequestTicketQuery(); } |
DOM Element ID
When the AJAX callback handler is executed, it sends the HTML response back to the frontend. On the frontend, a corresponding JS function receives the response (reqObject) and processes it. Finally it needs to find a proper DOM element in order to do some actions with it. That’s when you need a DOM Element ID.
var myelement = document.getElementById(elementID); |
See more: http://www.w3schools.com/js/js_htmldom_elements.asp
AJAX Call
When all parameters are known, you can perform an AJAX call. In WebUI a special THTMLB object is used for this.
thtmlbAJAXCall.callBackend(ajaxURL, ajaxCallbackFunction); |
You should be careful with visibility of the elements through different iframes and also in time.
JS Callback Function
You need a callback function to receive the response from your AJAX call, process it and update the corresponding DOM element. Below you can find an extract of a function used for asynchronous value fetch.
fetch: function(trigger, avf_event, csvSourceFields, rowIndex, customRef, csvTargetFields){ … thtmlbAJAXCall.callBackend(ajaxURL, this.update); … }
update: function(reqObject) { … var responseText = reqObject.request.responseText; var responseBlocks = responseText.split(“;”);
for(var i=0, len=responseBlocks.length; i<len; i++) { if(!responseBlocks[i]) { continue; }
var blockParts = responseBlocks[i].split(“,”); if(blockParts.length < 4) { continue; } var tagclassname = blockParts[0]; var elementid = blockParts[1]; var attributeName = blockParts[2];
var payload = thtmlbDecodeBASE64(blockParts[3]); var payloadValue = thtmlb_decode_html(payload); var targetElement = thtmlbGetElement(elementid); … case ‘tooltip’: if (targetElement.title != payloadValue) { targetElement.title = payloadValue; } … } |
Storing your JS Files
When developing your JS you need to make sure it is visible for your applications. In case you want to place a simple script to your page, you can do it right on the BSP page.
<!—Here you want to store some Javascript –> </script> |
You can also store it as a MIME object within your BSP application and include it on the page.
<script type=“text/javascript” src=“/sap/bc/bsp/sap/z<bspcmp>/scripts.js”></script> |
For relatively big applications or if you want your JS code to be visible for the whole WebUI framework, you can store the reference to your JS file in eth table WCFC_ADD_JS.
IMG à Customer Relationship Management à UI Framework à UI Framework Definition à Define Path for JS Files
Note: Last option is tested only in SAP CRM 7.0 EHP3.
Simple case
Let us practice first on a simple case, lets update a simple object link (<thtmlb:link>). As usual, we need to create a WebUI component, a view containing the link and a viewset.
When working with AJAX in WebUI you have to generate the view content via corresponding FACTORY methods.
<%@page language=”abap” %> <% |
In this case I just copied the standard JS AVF functionality (object thAVFMgr) into my own namespace and did desired modifications. You can also notice down that our DOM element is identified right on the page and the building of the right URL is a task for the copied JavaScript.
Here I am using my view controller class as a callback handler. This means that it must implement callback method: IF_CRM_WEB_CALLBACK~HANDLE_REQUEST. Update JS function (this.update) has not been changed.
method if_crm_web_callback~handle_request. data: lv_link_tag type ref to cl_thtmlb_link, data: lr_controller type ref to zl_zdmsh_aj_mainframe_impl. wait up to 3 seconds. if ir_controller is bound. lr_controller ?= ir_controller. lr_controller->gv_text = ‘Hello from AJAX !!!’. try. lv_link_tag->text = lr_controller->gv_text. lv_tag_class_name = ‘CL_THTMLB_LINK’. lv_html = lv_link_tag->if_bsp_bee~render_to_string( page_context = lr_controller->gv_thtmlb_element_link->m_page_context ). call function ‘SCMS_STRING_TO_XSTRING’ lv_html = cl_http_utility=>encode_x_base64( lv_html_xstr ). concatenate lv_tag_class_name ‘,’ lv_tag_parent_id ‘,’ lv_attribute_type ‘,’ lv_html ‘,’ lv_link_tag->id into lv_response. ir_server->response->set_cdata( |
How does it look like:
HTTPWatch trace:
It takes only 300 ms to build a page and 3 seconds later the link is updated.
Table Rendering
In WebUI most of the data is presented in tables, and therefore the option that is more desirable would be to render such kind of BSP elements. This topic however has been already described on SDN already, so see more at: http://scn.sap.com/community/crm/webclient-ui-framework/blog/2015/09/03/asynchronous-rendering-of-table-views . But here we will consider an option without any modification to SAP standard objects.
So, as usual, we need our WEBUI component, view set and the view. But also we need a context node, a value node in our case.
Our page TableView.htm looks like below:
<%@page language=”abap” %> thtmlbCCelleratorManager.createFastRowsCallback); |
You can notice that in our case we perform an AJAX call directly (thtmlbAJAXCall.callBackend); the task to build a proper URL is addressed to ABAP method CREATE_AJAX_URL; and we use a standard callback function thtmlbCCelleratorManager.createFastRowsCallback to process the AJAX response.
How to build a proper URL for AJAX call? If you are going to use the standard AJAX service handler, there are many things that you need to consider: a handler class, a controller id, security session token, etc. But all this is considered in the method CL_CRM_WEB_UTILITY=> CREATE_SERVICE_URL. So finally our method looks very simple:
method create_ajax_url. data: lr_class_desc type ref to cl_abap_typedescr. lr_class_desc = cl_abap_classdescr=>describe_by_object_ref( me ). call method cl_crm_web_utility=>create_service_url endmethod. |
In this case, we again use view controller class as a callback handler.
Next what we need is to implement IF_CRM_WEB_CALLBACK~HANDLE_REQUEST method. We do it in a very similar way as described here: http://scn.sap.com/community/crm/webclient-ui-framework/blog/2015/09/03/asynchronous-rendering-of-table-views. However, in our example, we are getting the data we need via parameter IR_CONTROLLER and therefore we do not need to change the standard SAP coding.
method if_crm_web_callback~handle_request. data: lr_controller type ref to zl_zdmsh_aj_tableview_impl. wait up to 3 seconds. if ir_controller is bound. lr_controller ?= ir_controller. if lr_controller is bound and lr_controller->fill_context_node( ). call method create_table_view_html_old ” Set Response “Invalidate ´content endif. |
Method CREATE_TABLE_VIEW_HTML is implemented in exactly the same way as it was described on SDN already. I just repeat it here for consistency.
method create_table_view_html. “ Constants “ variables “ strucures “ references “ field symbols “ create page instance “ rendering “ get model
while lv_count < lv_lines.
“Create AJAX content
concatenate `{ “rows”: [ ` lv_rows ` ], “fixedLeftRows”: [ ` lv_fixed_left_rows ` ], “fixedRightRows”: [ ` lv_fixed_right_rows ` ], “markedRows”: [ ` lv_marked_rows ` ], “tableId”: [ ‘` iv_table_id `’ ], “rowIds”: [ ` lv_row_ids ` ]}` into ev_html. endmethod. |
In the method FILL_CONTEXT_NODE we just populate our context node.
method fill_context_node. data: lt_but000 type table of but000. lr_wrapper = typed_context->somedata->get_collection_wrapper( ). lr_wrapper->clear( ). select * from but000 into table lt_but000 up to 2 rows loop at lt_but000 into ls_but000. create data lr_table_line. create object lr_valuenode lr_valuenode->set_properties( ls_but000 ). lr_wrapper->add( lr_valuenode ). endloop. endif. typed_context->somedata->build_table( ). endmethod. |
How does it look like:
3 seconds after…
Note that all your GET-methods are taken into consideration and you can navigate normally as you would do it in the normal WebUI table.
HTTPWatch trace:
It takes only 300 ms to build a page and 3 seconds later the table is updated.
P.S. See a second, better solution here: Asynchronous Rendering of TAJAX Areas / Table Views
Other Parts:
Parallelization in WebUI - Part 1
Parallelization in WebUI - Part 2
Parallelization in WebUI - Part 3
Hi Dimitri,
I've seen that you based your post on my initial topic on SDN: Asynchronous Rendering of Table Views.
By using the createFastRowsCallback function instead of a custom one there is no modification required, that's true. However I don't see where you get rid of initial line which is generated in the standard due to the empty collection in the first run.
Best regards,
Sebastian
Hi Sebastian,
Yes, you are right. But it was quite some time ago and i do not remember. I quickly tried to recover, but my old solution has stopped working and i do not have too much time to investigate it.
Instead I have posted a blog post about the new solution, which is working. It's here:
Asynchronous Rendering of TAJAX Areas / Table Views
There the use of Fast Row Creation feature is not needed. And it's much leaner now, as it's working with inner HTML of the corresponding tajax area (can be a table as well).
There I have posted the coding, and it does not generate the initial line. Hope it helps you.
As a top secret, I can tell you (nobody else, please keep it with you) that SAP development liked these AJAX features and therefore we can probably expect something coming from the standard. But no guarantee, concrete plans or deadlines can be given so far.
BR, Dima
Hi Dimitrii,
my initial post including the modification was sort of a proof of concept anyway. However, I appreciate that this kind of functionality might be part of the standard. I assume that you're closer to the SAP development than I am, so it would be great whether you could tell me when a feature like this is announced.
Best regards,
Sebastian
Sure, I will remind them to make some posts.