Saturday 3 September 2011

Overloading %SubmitScriptName

As discussed in the previous post, all actions in a component that eventually require a server trip would invoke %SubmitScriptName for submitting the page. As such, it sometimes provide a convenient point for performing javascript modifications on the page.

This is achieved by storing a reference to the original function, and redefining %SubmitScriptName to perform your custom logic — invoking the original function as needed.

The following sample scenarios may help to illustrate this technique.

Objective: Display a javascript confirmation dialog (i.e., an Ok/Cancel pop-up dialog) prior to performing an action. In this example, the dialog is displayed when the component Save action is invoked.

Note that in this problem, it’s not possible to use the PeopleCode MessageBox function because the message generated by PeopleCode for Ok/Cancel will not be a pop-up dialog. It is also inefficient, because it requires a page refresh.

Sample Solution: Insert an HTML Area at level 0 of desired page, with the following content:

Explanation: When the script above is executed on the body of the page, a reference to the original function is stored in orig_%SubmitScriptName. %SubmitScriptName is then redefined to a custom function. Now when the user performs an action and %SubmitScriptName is invoked, it would be the custom function that gets called.

Inside the custom function, it checks if the action being performed is the targeted action. If so, then a confirm dialog is presented. The custom function exits without invoking the original function if the cancel option was selected. The above coding trick is possible because functions are objects in javascript. The .apply method is a javascript feature that allows orig_%SubmitScriptName to be invoked with the same arguments used to call the current function.

Objective: When the page is submitted, lock all fields, buttons and links to prevent the user from resubmitting the page while the server is processing the request.

Sample Solution: Insert an HTML Area at level 0 of desired page, with the following content:

Explanation: After the original function is invoked, the modified function loops through all the visible form fields (this would include the buttons) and disable them. It is important that the disabling of fields is performed after the form is submitted. This is because browsers do not include disabled fields on the data sent to the server.

Afterwards, the modified function loops through all the hyperlinks and disables them by canceling the default action of the click event.

Objective: There is currently no PeopleCode events associated with scroll actions. For some reason, you want to detect in PeopleCode whenever the user performs a scroll action.

Sample Solution: Create a subpage with the following fields:EVENT_LOG_SBP subpage

Here, the 2 editboxes are invisible with Modifiable by JavaScript property set to checked. Insert the subpage at the level 0 of desired page. Set the content of the HTML Area with the following:

Now on the PeopleCode side, the following template code could be used on DERIVED_WORK.ACTION_DESCR.FieldChange:

If All(DERIVED_WORK.ACTION_DESCR) Then   Evaluate DERIVED_WORK.ACTION_DESCR   When = "hpers"      /* Personalize action was performed */      Break;   When = "hfind"      /* Find action was performed */      /* Search text can be retrieved using %Request.GetParameter("ICFind") */      Break;   When = "hviewall"      /* View All action was performed */      Break;   When = "hexcel"      /* Download to Excel action was performed */      Break;   When = "htop"      /* View First action was performed */      Break;   When = "hup"      /* View Previous action was performed */      Break;   When = "hdown"      /* View Next action was performed */      Break;   When = "hend"      /* View Last action was performed */      Break;   When = "htab"      /* Show All Columns action was performed */      Break;   When-Other      Evaluate Left(DERIVED_WORK.ACTION_DESCR, 3)      When = "srt"         /* Sort Column action was performed */         /* Column # is Substring(DERIVED_WORK.ACTION_DESCR, 4, 1) + 1 */         Break;      When = "tab"         /* View Tab action was performed */         /* Column # is Substring(DERIVED_WORK.ACTION_DESCR, 4, 1) + 1 */      End-Evaluate;   End-Evaluate;End-If; REM Clear the hidden field buffers;DERIVED_WORK.PAGE_FIELD_NAME.SetDefault();DERIVED_WORK.ACTION_DESCR.SetDefault();

Explanation: Prior to invoking the original %SubmitScriptName function, the modified function inspects the value of the 2nd parameter (arguments[1]). If the pattern matches a scroll action: DERIVED_WORK.PAGE_FIELD_NAME is populated with the scroll page field name, and DERIVED_WORK.ACTION_DESCR is populated with the action code.

On the server side, DERIVED_WORK.ACTION_DESCR.FieldChange is executed because of the change. The 2 hidden fields’ values needs to be cleared at the end of the PeopleCode, in order to capture when the same scroll action is performed consecutively.

Take Note: On the server side, the FieldChange event is executed before the action is actually performed. This means that if you check the TopRowNumber property of the rowset when a View Next is performed, you won’t get the new value.

The above 3 sample solutions can be combined into one. See this in action.

When performing the modification on %SubmitScriptName as illustrated above, it may be rightful to be concerned about future incompatibilities. For example, what if a future service pack or version changed the parameters for %SubmitScriptName? Would it totally break your component?

Because of this, it is prudent to check that scripts calling %SubmitScriptName are passing our expected parameters. Otherwise then, the modification is skipped. This can be achieved by performing the following check at the start of the redefined %SubmitScriptName function:

  %SubmitScriptName = function() {    // invoke the original function if the parameter signature changed    if (arguments.length!=2 ||     !(arguments[0].nodeType && arguments[0].nodeType==1 && arguments[0].tagName && arguments[0].tagName=='FORM') ||     !(typeof(arguments[1])=='string')) {       orig_%SubmitScriptName.apply(this,arguments);      return;    }

Also, the sample solutions presented above have the javascript embedded in the HTML Area as constant. This is just to simplify illustration. The best way is to place javascript code in an HTML definition and link to it using a %JavaScript meta-HTML, as described here.

Related Posts

No comments:

Post a Comment