CRM and CX Blogs by SAP
Stay up-to-date on the latest developments and product news about intelligent customer experience and CRM technologies through blog posts from SAP experts.
cancel
Showing results for 
Search instead for 
Did you mean: 
dmitry_sharshatkin
Active Participant


Asynchronous Saved Search Fetch



I have noticed it could be a nice implementation variant for AJAX queries in WebUI. Everyone has it’s own saved search and everyone would like to know how many items are behind it. (Click a picture below to see how it's working :smile: ).





Implementation



Create Custom Controller


Saved searches are managed within GS_WC_QLINKS component. What we are going to need is a new custom-controller e.g. counter.do.




 


This controller will be called stateless to calculate the number of entries behind each of the saved search.




Perform AJAX request


 


The AJAX request is called right from the view GS_WC_QLINKS/savedsearches.htm.


 










<%@page language="abap" %>


<%@extension name="htmlb" prefix="htmlb"   %>


<%@extension name="xhtmlb"   prefix="xhtmlb" %>


<%@extension name="thtmlb" prefix="thtmlb"   %>


<%@extension name="crm_bsp_ic"   prefix="crmic" %>


<%@extension name="bsp" prefix="bsp"   %>


<%


  DATA  lr_ss_wrapper        TYPE REF TO cl_bsp_wd_collection_wrapper.


  DATA  lr_ss                TYPE REF TO cl_crm_bol_entity.


%>


<thtmlb:overflowContainer verticalScrollbar="NEVER" >


  <%


  data:   lv_search_id    type string.


  data:   lv_search_name     type string.


  lr_ss_wrapper  = savedsearches->get_collection_wrapper( ).


  lr_ss ?= lr_ss_wrapper->get_first( ).


  If lr_ss  is not bound.


  %>


   <img  class="th-clr-img" src="<%= CL_THTMLB_UTIL=>GET_DYN_ICON_URL(   EXPORTING iv_icon_name =   CL_THTMLB_UTIL=>GC_ICON_INFOMESSAGE iv_icon_type  = CL_THTMLB_UTIL=>GC_BUTTON_ICON ) %>" />&nbsp;


   <thtmlb:textView text = "<%= otr(CRM_UIU_BT/NORESULT)   %>" />


  <%


  Endif.


  while  lr_ss is bound.


  lv_search_id  = lr_ss->get_property_as_string( iv_attr_name = 'GUID' ).


  lv_search_name  = lr_ss->get_property_as_string( iv_attr_name = 'DESCRIPTION' ).


  %>


  <thtmlb:link id        = "<%= lv_search_id %>"


               onClick = "GO"


               onClientClick  = "thAdvSearchRegisterEvent('GO')"


               text    = "<%= lv_search_name  %>"


               tooltip = "<%= lv_search_name %>" />


  <%


  lr_ss ?= lr_ss_wrapper->get_next(  ).


  %>


  <br>


    <!-- -->


  <script language="javascript" type="text/javascript">


   thtmlbAJAXCall.callBackend("<%= controller->create_ajax_url(   iv_ss = lv_search_id )   %>",UpdateSavedSearchCallback);


  </script>


<!--   -->


  <%


  endwhile.


  %>


</thtmlb:overflowContainer>




Build Stateless AJAX URL


 


Saved search IDs are provided along with the AJAX URL, which is built as shown below.


 










  method create_ajax_url.

data: lv_query     type string.
data: lv_url       type string.

" Build Query
concatenate lv_query 'crm_controller=' me->component_id into lv_query.

if iv_ss is not initial.
concatenate lv_query '&saved_search=' iv_ss into lv_query.
endif.

" Create State - Less URL
call method cl_crm_web_utility=>create_url
exporting
iv_bsp_application
= me->application_name
iv_bsp_page       
= 'counter.do'
iv_query          
= lv_query
iv_in_same_session
= abap_false
iv_no_cookie      
= space
iv_cache_busting  
= 'X'
receiving
ev_url            
= lv_url.

split lv_url at '?' into lv_url lv_query.

call method runtime->get_url_stateless
exporting
application_name
= me->application_name
page_name       
= 'counter.do'
receiving
url             
= lv_url.

concatenate lv_url '?' lv_query into rv_url.

endmethod.




Process AJAX Requests on the backend


The requests will be processed by our custom controller. There we need two methods: DO_REQUEST and GET_COUNT.




 


DO_REQUEST:


 










  method do_request.

call method super->do_request.

" Data
data: lv_controller_id    type string.
data: lv_saved_search     type string.
data: lv_key              type guid_32.
data: lr_core             type ref to cl_crm_bol_core.
data: lr_shortcut_man     type ref to cl_crm_shortcut_manager.
data: lr_shortcut         type ref to cl_crm_bol_entity.
data: lt_field_list       type table of crmt_rtd_string_pair.
data: ls_field_list       type crmt_rtd_string_pair.
data: ls_count            type string.

" Containers
data: lr_writer           type ref to cl_sxml_string_writer.
data: lv_json             type xstring.

" Get URL parameters
lv_controller_id 
= request->get_form_field( 'crm_controller' ).
lv_saved_search  
= request->get_form_field( 'saved_search' ).


" Get Shortcut Data
lr_core
= cl_crm_bol_core=>get_instance( ).
"lr_core->start_up('SO2').                                        " Needed to avoid ABAP dumps
lr_core
->start_up('ALL').

lr_shortcut_man
= cl_crm_shortcut_manager=>get_instance( ).

if lv_saved_search  is not initial and
lv_controller_id
is not initial.
lv_key
= lv_saved_search.
lr_shortcut
= lr_shortcut_man->get_shortcut_by_key( iv_key = lv_key ).

if lr_shortcut is bound.

" Get the name
concatenate lv_controller_id lv_saved_search into ls_field_list-name separated by '_'.
translate ls_field_list-name to upper case.

" Get the value
ls_field_list
-value = lr_shortcut->get_property_as_string( 'DESCRIPTION' ).

ls_count
= get_count( lr_shortcut ).
if ls_count is initial.
ls_count
= '?'.
endif.
concatenate '(' ls_count ')' into ls_count.
condense ls_count no-gaps.

concatenate ls_field_list-value space ls_count
into ls_field_list-value respecting blanks.

else.
ls_field_list
-name  = 'ERROR_IN_SAVED_SEARCH_'.
ls_field_list
-value = 'NO_OBJECT_'.
endif.
else.
ls_field_list
-name  = 'ERROR_IN_SAVED_SEARCH_'.
ls_field_list
-value = 'NO_GUID_'.
endif.


" Collect Response
append ls_field_list to lt_field_list.

"ABAP to JSON
lr_writer
= cl_sxml_string_writer=>create( type = if_sxml=>co_xt_json ).
call transformation id source text = lt_field_list[] result xml lr_writer.
lv_json
= lr_writer->get_output( ).


" Send response
response
->set_header_field( name = 'content-type'  value = 'text/xml' ).
response
->set_header_field( name = 'charset'       value = 'utf-8' ).
response
->set_header_field( name = 'Pragma'        value = 'no-cache' ).
response
->set_header_field( name = 'Expires'       value = '0' ).
response
->set_header_field( name = 'Cache-Control' value = 'max-age=0' ).

response->set_data( lv_json ).

endmethod.




GET_COUNT:


 










  method get_count.

data: lr_dquery            type ref to cl_crm_bol_dquery_service.
data: id_of_query_template type genil_dquery_handle.
data: lr_res_col           type ref to if_bol_entity_col.

rv_count
= ''.

check: ir_shortcut is bound.

try.
id_of_query_template
= ir_shortcut->get_property_as_string( 'PARAMETER_' ).
lr_dquery
= cl_crm_bol_dquery_service=>load_query_template( iv_query_id =


                                                                     id_of_query_template ).

lr_res_col
= lr_dquery->get_query_result( ).
rv_count
= lr_res_col->get_iterator( )->size( ).

catch cx_crm_genil_not_ready.
catch cx_sy_no_handler.
catch cx_crm_bol_not_ready.
catch cx_bol_exception.
endtry.

endmethod.




Writing AJAX call-back function













function UpdateSavedSearchCallback(reqObj)


  {


   var responseText = reqObj.request.responseText;


   var obj = JSON.parse(responseText);


   var fnum = obj.TEXT.length;


   for (var i = 0; i < fnum; i++) {


    var elem = document.getElementById(obj.TEXT[i].NAME);


     if (elem){


      elem.title = obj.TEXT[i].VALUE;


      elem.innerHTML  = obj.TEXT[i].VALUE;


     }


   }


  }




Publish a link to the JS file in the table WCFC_ADD_JS.




1 Comment