Additional Blogs by Members
cancel
Showing results for 
Search instead for 
Did you mean: 
former_member184549
Participant
0 Kudos

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=... Table with navigation plans|width=699|src=https://weblogs.sdn.sap.com/weblogs/images/251792747/js_3_abap_table_navig_plans.PNG!

3 Comments