Skip to Content

Often there is a wish to make CRM trigger a dynamic navigation when specified logical conditions are met. A good example would be if we wanted to click the “confirm account” button automatically once a suitable account was found. Whilst this can be accomplished by passing relevant BOL entites into data collections and then placing these into the navigation queue of the window, the flexibility afforded by the BSP system allows us to also use javascript to accomplish the same. This post describes a system for accomplishing exactly that, and also includes a method for generating such javascript code from ABAP.

Let’s commence with a simple example, making the “confirm account” button below automatically click itself after the view has been loaded:

Automatic button click

The following javascript code can achieve this:

new PeriodicalExecuter(function(pe1) {

    this.counter = this.counter ? this.counter : 1;

    this.callcnt = this.callcnt ? this.callcnt + 1 : 1;

    if (this.callcnt > 99 ) {

        this.counter = 100;

    }

    if (this.counter == 1) {

        var ele1 = fuzzyId(“th-tb”, “ul”, “a”, “Confirm”, 1);

        if (ele1) {

            this.counter = this.counter + 1;

            ele1.simulate(“click”);

        }

    }

    if (this.counter > 1) {

        pe1.stop();

    }

}, 0.10);

This code would need to be inserted in the view of the BSP:

View of BSP

Obviously, you can code what conditions must be met for the automatic “click” by inserting conditional ABAP code around the above javascript block.

When the above javascript is executed by the browser, a call to the PeriodicalExecutor function is made every 0.1s (provided there is not another instance of that function already running). If the periodical executor has still not yet managed to complete after 99 calls it will terminate the execution plan, in other words it has approximately 10 seconds (0.1 * 99) to complete. In the above example, it only has to execute a single-step “click plan” but a more complicated four-step plan will be described later in this document. The above code tries repeatedly to find the element for the “confirm account” button (it will wait for it to appear if necessary), and once it has found the element a click event on the element is simulated. Since this is the only step in the plan the periodical executor is then stopped. As this is javascript running in the client browser it will not impact on server performance of other connected clients.

An important part of the code, is the identification of the document element for the “confirm account” button. In order to determine this element it is recommended to use tools such as Internet Explorer developer tools (for IE) or Firebug (for Firefox). This screenshot show how Firebug identified the “Confirm Account” button:

 

Firebug analysis of confirm account

The element we’re interested in locating has an tag with ID of “C2_W11_V12_V35_V34_Confirm”. This element is the link that we’ll be interested in simulating a click for. We can’t assume the prefix will always remain “C2_W11_V12_V35_V34”, but rather must assume it will change. It will change for instance if BSP  component usage changes and its composition is related to the order in which the component/window/viewset/view controllers were created.

The fuzzyId function enables us to deal with the changing prefix. There are two ways in which fuzzyID works. We could simply call it like this:

        var ele1 = fuzzyId(“”, “”, “a”, “Confirm”, 0);

In which case it will try and identify the element by tag “a” and a regular expression match on the element “Confirm” (although “Confirm” is a simple string match in this case, it is actually converted into a regular expression). The other way in which fuzzyID could find the element is like this:

        var ele1 = fuzzyId(“th-tb”, “ul”, “a”, “Confirm”, 1);

In this case, we also provide 3 additional parameters: the class of a parent element (“th-tb”) and/or the tag of the parent element (“ul”) and the number of levels from the parent element to the element we’re interested which is “1” (since javascript counting starts with zero, this actually means that you count 2 elements between parent element and target element, so “ul”, “li” and then “a”).

I’m not certain which of the above approaches is better. It is necessary to try and balance simplicity for developers who may struggle with regular expressions with the need to make the element search code rugged and reliable enough to cope with unrelated system changes. Relying on just the tag name and a simple regular expression to locate the element may not work if there are similarly named elements in the document, for instance a tagname of “a” and regular expression of “Confirm” might also find elements like here  (http://www.prototypejs.org/download)A protoype addon for simulating events, event.simulate.js, found here  (https://github.com/kangax/protolicious/blob/master/event.simulate.js)0.1.

My own custom javascript library that allows for fuzzy identification of elements, which can be downloaded here  (http://bloggroll.com/stat/fuzzy.js)

Prototype was chosen in order to simplify the cross-browser javascript (primarily for the PeriodicalExecutor) but also because of scriptaculous integration should it be necessary to provide user feedback at a later stage. Jquery  (http://jquery.com/) could also have been chosen as an alternative to prototype. Note that it is possible to perform the fuzzy element search by callling function fuzzyElementSearch from fuzzy.js without any additional javascript library, so prototype is mostly used here as a convenience library to facilitate rapid javascript coding.

<br /><br/>The three javascript libraries above need to be saved to the SAP server using SE80's MIME repository. The choice of /sap/bc/bsp/sap/crmcmp_ic_frame/scripts/common as server path can be changed to another path should this be desired. After uploading, it is worth testing that they can be downloaded from a web browser.<br/><br/>Moving on from the simple one-step navigation solution, a working four-step navigation plan is as follows:<br/><br/><script type="text/javascript"><br/>new PeriodicalExecuter(function(pe1) {<br/>    this.counter = this.counter ? this.counter : 1;<br/>    this.callcnt = this.callcnt ? this.callcnt + 1 : 1;<br/>    if (this.callcnt > 99 ) {<br/>        this.counter = 100;<br/>    }<br/>    if (this.counter == 1) {<br/>        var ele1 = fuzzyId("", "li", "a", "ZIC_BT_IHI$", 0);<br/>        if (ele1) {<br/>            this.counter = this.counter + 1;<br/>            ele1.simulate("click");<br/>        }<br/>    }<br/>    if (this.counter == 2) {<br/>        var ele2 = fuzzyId("th-ip-sp", "div", "input", "_btquery_struct.object_id", 0);<br/>        if (ele2) {<br/>            this.counter = this.counter + 1;<br/>            ele2.value = "<%= gv_value %>";<br/>            ele2.simulate("change");<br/>        }<br/>    }<br/>    if (this.counter == 3) {<br/>        var ele3 = fuzzyId("th-gr-td", "td", "a", "_SEARCH$", 0);<br/>        if (ele3) {<br/>            this.counter = this.counter + 1;<br/>            ele3.simulate("click");<br/>        }<br/>    }<br/>    if (this.counter == 4) {<br/>        var ele4 = fuzzyId("th-clr-cel", "td", "a", "_ResTable_sel_1-rowsel$", 0);<br/>        if (ele4) {<br/>            this.counter = this.counter + 1;<br/>            ele4.simulate("click");<br/>        }<br/>    }<br/>    if (this.counter > 4) {<br/>        pe1.stop();<br/>    }<br/>}, 0.10);<br/></script><br/><br/>

Note that even though this navigation plan is launched from a view of one component, the javascript will continue to run in the client’s browser even as other components and views are rendered, so it is possible to perform automated navigation without component enhancement. The periodical executor would stop however if navigation to a different document iFrame took place, which occurs for instance if the transaction launcher is invoked.

<br /><br/>Other than performing three clicks on identified elements, the more complex navigation plan also sets the value of a field on the screen in step 2 and then performs a simulation of the browser onChange event so that the backend is notified of value entry. The value set in step 2 comes from an ABAP variable, gv_value that is within the <%= ... %> non-evaluated angular brackets.<br/><br/>

The javascript code is regular and follows a repeatable pattern. Instead of inserting javascript into the view, it is possible to generate the javascript code from ABAP. This saplink  (http://code.google.com/p/saplink) nugget archive  (http://bloggroll.com/stat/javascript_generator.zip) contains the code for a class that can generate the necessary javascript. The class is driven by a driver table which contains the navigation step information for the javascript that will be generated:

!https://weblogs.sdn.sap.com/weblogs/images/251792747/js_3_abap_table_navig_plans.PNG|height=189|alt=DDIC Table with navigation plans|width=699|src=https://weblogs.sdn.sap.com/weblogs/images/251792747/js_3_abap_table_navig_plans.PNG!

To report this post you need to login first.

3 Comments

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

  1. Micha Van Nijen
    Hi Jonathan,

    This is fantastic. I’m going to try this out and if this is as easy as it seems, I’m going to have lots of fun with it.

    The less server requests as possible, the better!

    thanks for this enormously interesting blog!

    Kind regards,

    Micha Van Nijen

    (0) 
    1. Jonathan Groll Post author
      Hi Micha,

      Thanks for the comment. For trying it out, may I suggest you first start with a simple navigation like the first example, and then work your way up to using an ABAP class to generate the javascript? Let me know how it goes.

      Also, this method won’t reduce server requests by the way. For every click that gets simulated the SAP server will still have to respond in the same way as if the user had actually clicked the button. In other words, a simulated click is just like a real click and will still result in a server request.

      More detailed explanation: I guess where it gets confusing is the PeriodicalExecutor getting called 99 times over a ~10 second interval. Let’s say we are running a 4-step navigational plan. Every time the periodical executor gets called it tries to run the current step of the navigational plan. So if there are only 4 steps it only needs to be called 4 times in order to do what it needs to do, why then do we call it up to 99 times? The answer is often the browser is not yet ready for the current step of the navigational plan. So we are waiting for an input box to appear on screen in step 2 say, and until that input box has appeared on the screen we can’t fill in the value in that input box that we need to do on the current step of the navigational plan. So, nothing is done for that call and we keep on trying approximately every 0.1s and will try up to 99 times. There is no server load for each of the 99 tries because the javascript is running client side in the user’s browser, and all it does is check if the element exists in the DOM. So, these 99 calls are very ‘light’ and do not impact the local browser much and only result in a server call when there is an action from the navigational plan that can be performed, which in the case of a four step navigational plan will be only 4 times. And for each of these 4 calls a server request will result, exactly as it would result if the user had performed the same action manually.

      Cheers,
      Jonathan

      (0) 
  2. Stephen Johannes
    As much as I like technically interesting solutions like this, we have to remember the drawback of BDC approaches.  If SAP changes how the page is generated between notes, support packages, or releases this could break.

    I would probably also only recommend this type of approach if component enhancement can’t solve the problem.

    Take care,

    Stephen

    (0) 

Leave a Reply