Web Generate Mass Updates with Designer

By Hervé Deschamps, Oracle Corporation.

This article presents a solution to yet another one of these problems that keeps cropping up on just about every single application development project. Very often users ask for the ability to apply the same update to a larger number of records with one mouse click.
Figure 1 illustrates what you can get Designer to generate for you. The web page is composed of three frames. Users can select a number of records using the module's query criteria displayed in the top frame and pressing the find button. The records that match that criteria are displayed in the bottom left frame. The bottom right frame is used to display the details of a specific record within the list.
Figure 1
Illustrates the end result that we want to achieve.
So far, this is all straightforward Designer functionality. The real value of this article lies in the technique demonstrated to add the items displayed below the button Find and Clear. When users type a value for Monitor Code and click on the button 'Apply Monitor Code to All Selected Records', all records selected by the query criteria in the top frame will get updated with the new monitor code and a feedback message will be displayed on a new web page.

Step 1: Getting the basic module design.

The module illustrated in Figure 1 is a simple one table usage module. Figure 2 shows the module data diagram of our module.
 
Figure 2
Module Data Diagram of the module shown in Figure 1.







Step 2: Getting the frame structure required.

Users need to be able to see both the query criteria and the record list on the same web page so that they can quickly get the list of records desired. So we need to get Designer to generate frames. In Design Editor, double click on the module component. Ours is called PIV_NAT_HEADER. A dialog pops up on the screen. The dialog title is "Edit Module Component <module component name>". If you do not get this dialog but see the property palette instead, select menu option: Options -> Use Property Dialogs and double-click on the module component again. Choose tab Display, pick layout style List/Form.

Also in Design Editor, select the module, right-click on it and bring up the Generator Preferences. Expand the Frames preferences and set 'Place Query Form on Separate Page' to No.

At this point, if we generate the module we get the layout illustrated in Figure 1, except the monitor code and the apply button.
 
 

Step 3: Adding one or more extra fields and a button to the query frame.

One would think that it is possible to use unbound items for this. Unbound items are custom items that are not based on a table column in the database, like a button or a derivation expression. Well, Designer 2.1.2. does not allow us to create unbound items of type Button for web modules yet. The only available display types are: Check Box, Image, Pop List, Radio Group, Text and Text List. Also, if you try to include an unbound text item in the query form, it will work until you type a value in the unbound field at runtime. This is the type of error message that you'll get:

Error!
The following unhandled error has occurred in the routine
gbamebw2_hdd$piv_nat_header.QueryHits:
ORA-00936: missing expression

This is because of the way WSGL.BuildWhere is used. WSGL is a package provided by the Designer development team. Some people dare to modify that package. We don't. We prefer to find other ways.

Here is a technique to add a text item with prompt and a button. This one always works. In the Design Editor double-click on the module component. Select the tab User Text. Click on Bottom of query form. Click on the Text Editor button. Listing A shows what we type in the editor for our first shot at getting the 3 items shown in Figure 1 displayed on our web page.
 

Listing A


<FORM ACTION="gbamebw2_hdd$piv_nat_header.apply_monitor" METHOD="POST" TARGET="_top">
<B>Monitor Code: </B><INPUT TYPE="text" NAME="P_MONITOR_CODE" SIZE="10">
<input type="button" value="Apply Monitor Code to All Selected Records">
</FORM>

HTML code to define the basic elements of the mass update.
This is a lot of code for a text item and a button isn't it? And you haven't seen half of it yet. What we are trying to do here is to submit a job to the web server when the user pushes a button. We also want to send a bunch of parameters with the submission. This requires the use of an HTML form. You can see the begin and end form tags in listing A: <FORM> and </FORM>. In HTML the end tag is the same as the begin tag with the addition of a forward slash. Target designates the location where the web server's response should be displayed. _top means: clear the current window and use it to display the response. Method specifies the way parameter will be sent to the server. We advise you to always set it to POST unless you have a good reason to do otherwise.

Ignore the Forms Action for now. We will explain this later.

The first line of HTML code in Listing A defines the text field and its prompt. The next line defines the button. The label of the button is the text in the value property. The advanced reader will notice that we chose not to use the item type SUBMIT.  This is because we may not always want to submit the HTML form when users press the apply button. More on this later.

To include the HTML code from Listing A into Designer, first choose the appropriate module component in Design Editor. Most modules have only one. Double click on it. A dialog pops up on the screen. The dialog title is "Edit Module Component <module component name>". If you do not get this dialog but see the property palette instead, select menu option: Options -> Use Property Dialogs and double-click on the module component again. In the User Text tab, select "Bottom of the Query Form" and enter the code in Listing A. Figure 3 shows you what you should see on the screen.
 

Figure 3
Setting up an extra text field and button in Designer


Two side notes:

Also note that at this point you have free hand to make the layout look as pretty as HTML will let you. You are no longer constrained by Designer.
 

Step 4: Testing the layout.

At this point, we can generate the module and bring it up in the browser. We get the same layout as Figure 1 illustrates. But when we click on the apply button, nothing happens. How do we change this? Next step.
 
 

Step 5: Getting the Apply Button to do something.

In order for something to happen when users click on the apply button we need to add an event on the button. The code for this is shown in listing B. It is a modification of the code shown in Listing A.

When users press the apply button, the browser runs the code specified against the onClick event. This event provides us with a mechanism to implement form level validation or derivation that can be operated inside the browser. Any business logic that can be performed in the browser saves network traffic and provides user with immediate feedback. Such logic can be programmed in JavaScript. A word of caution about this language. It is young and still growing up. For example, there are inconsistencies in the way certain commands work from browser to browser and from older browser versions to newer ones. These differences have been ironed out for core JavaScript constructs, that is why we recommend restricting yourself to those, unless you are absolutely sure that all users of your system will use a specific browser version, forever. Some programmers prefer to minimize the amount of JavaScript and do all the validation in the server, using our mature and stable PL/SQL. This will work but users generally prefer immediate feedback (field level validation) and networks can only take so much traffic.

In Listing B the onClick event calls JavaScript function monitorOnClick that we have still to write. More on this later.

Listing B


<FORM ACTION="gbamebw2_hdd$piv_nat_header.apply_monitor" METHOD="POST" TARGET="_top">
<INPUT TYPE="hidden" NAME="P_SERIAL_NUM">
<INPUT TYPE="hidden" NAME="P_FULL_GRANT_NUM">
<INPUT TYPE="hidden" NAME="P_APPL_ID">
<INPUT TYPE="hidden" NAME="P_FY">
<INPUT TYPE="hidden" NAME="P_LAST_NAME">
<INPUT TYPE="hidden" NAME="P_FIRST_NAME">
<INPUT TYPE="hidden" NAME="P_ORG_NAME">
<B>Monitor Code: </B><INPUT TYPE="text" NAME="P_MONITOR_CODE" SIZE="10">
<input type="button" value="Apply Monitor Code to All Selected Records" onClick="monitorOnClick(this)">
</FORM>

Complete HTML code to define the basic elements of the mass update.



Step 6: Passing parameters to the mass update procedure.

Looking at listing B you have probably guessed that the mass update is performed by the packaged procedure gbamebw2_hdd$piv_nat_header.apply_monitor that we will write very soon. apply_monitor will issue a simple update statement. It will change the monitor code of all the records that match the query criteria specified in the top frame of the web page. In this step we show how to pass the query criteria and the monitor code to apply_monitor as parameters.

Listing B has all the code that we need. All parameters to be passed down to apply_monitor have to be declared as input items in our HTML form. The monitor code was already defined in Listing A. We added the query criteria items as hidden fields. These fields were already defined in the HTML form generated by Designer. We need to duplicate them in our HTML form, so we create them as hidden items in order not to confuse the users with duplicate information

We also need to write some JavaScript code to populate the hidden fields composing our form. The mechanism for doing so is to use the onClick event against our apply button to call a JavaScript function that will copy data from the visible HTML items to their invisible counterparts. We mentioned this function already: monitorOnClick.

JavaScript identifies forms with numbers. The first form gets number 0. The second form gets number 1. The form that was generated by Designer is called forms[0] in JavaScript. Our custom form is forms[1]. As a side note, it is a very bad idea to position a form before the one generated by Designer. The client validation code generated by Designer refers to data fields as forms[0].<field_name> instead of <form_name>.<field_name>. If one of your forms takes number [0], the JavaScript code generated by Designer will produce runtime errors or produce behavior anomalies.

Listing C shows the JavaScript code that transfers data from forms[0] to forms[1] and then submits our HTML form. We placed this code in front of the code shown in Listing B.
 

Listing C


<SCRIPT>
function monitorOnClick(ctl) { //ctl is not really used in called function
 // Copy entered values in forms0 to hidden fields of forms1
 document.forms[1].P_SERIAL_NUM.value  = document.forms[0].P_SERIAL_NUM.value;
 document.forms[1].P_FULL_GRANT_NUM.value  = document.forms[0].P_FULL_GRANT_NUM.value;
 document.forms[1].P_APPL_ID.value  = document.forms[0].P_APPL_ID.value;
 document.forms[1].P_FY.value  = document.forms[0].P_FY.value;
 document.forms[1].P_LAST_NAME.value   = document.forms[0].P_LAST_NAME.value;
 document.forms[1].P_FIRST_NAME.value  = document.forms[0].P_FIRST_NAME.value;
 document.forms[1].P_ORG_NAME.value  = document.forms[0].P_ORG_NAME.value;
 document.forms[1].submit();
 return true;
}
</SCRIPT>

JavaScript code of to transfer data before running the mass update.
You may want to add more code to this JavaScript function to make the web page more user friendly. For example it would be wise to ensure that  users do not press the apply button without a query criteria. This would update the entire table!
 

Step 7: Testing the module.

At this point if we generate the module and click on the apply button, something does happen. We get the error message displayed in Figure 4
 
Figure 4
Normal error message: need to do a bit PL/SQL coding.
When submitted, our HTML form calls procedure gbamebw2_hdd$piv_nat_header.apply_monitor. We have yet to create it.
 
 

Step 8 (Last): Creating the mass update procedure.

The last thing we need to do is create the procedure apply_monitor in Designer. Figure 5 shows where we defined it in the Navigator.
Figure 5
Illustrates where to place custom PL/SQL in Designer.


We are now into familiar territory: PL/SQL programming. Listing D shows and example of what you may want to do with the parameters passed. You will probably need more elaborate logic than the one we present here. For example there may be state transition business rules on the monitor code. That is it may not be permitted for the monitor code to change from one value to another. In that case you will have to use a cursor and accept or reject record updates. If you reject some updates you need to decide whether you reject the entire mass update or proceed with the update of records that do not violate any business rules.

Once all this is decided and coded, you will need to report the results to the user. Again, you will probably decide to provide something more elaborate than our 2 htp.p statements in Listing D. In this article we limit ourselves to a proof of concept.
 

Listing D


PROCEDURE apply_monitor (
      P_SERIAL_NUM IN VARCHAR2,
      P_FULL_GRANT_NUM IN VARCHAR2,
      P_APPL_ID IN VARCHAR2,
      P_FY IN VARCHAR2,
      P_LAST_NAME IN VARCHAR2,
      P_FIRST_NAME IN VARCHAR2,
      P_ORG_NAME IN VARCHAR2,
      P_MONITOR_CODE IN VARCHAR2
  ) IS
BEGIN
  UPDATE appl_gab_monitor_codes_t age
  SET age.gme_code = P_MONITOR_CODE
  WHERE age.appl_id IN (
 SELECT piv.appl_id
 FROM  x_pv_grant_pi piv
 WHERE (piv.full_grant_num LIKE NVL(P_FULL_GRANT_NUM,'%') OR (P_FULL_GRANT_NUM IS NULL AND piv.full_grant_num IS NULL)) AND
       (piv.serial_num LIKE NVL(P_SERIAL_NUM,'%') OR (P_SERIAL_NUM IS NULL AND piv.serial_num IS NULL))AND
       (piv.appl_id LIKE NVL(P_APPL_ID,'%')) AND
       (piv.fy LIKE NVL(P_FY,'%') OR (P_FY IS NULL AND piv.fy IS NULL)) AND
       (piv.last_name LIKE NVL(P_LAST_NAME,'%') OR (P_LAST_NAME IS NULL AND piv.last_name IS NULL)) AND
       (piv.first_name LIKE NVL(P_FIRST_NAME,'%') OR (P_FIRST_NAME IS NULL AND piv.first_name IS NULL)) AND
       (piv.org_name LIKE NVL(P_ORG_NAME,'%') OR (P_ORG_NAME IS NULL AND piv.org_name IS NULL))
                      );
  COMMIT;

  htp.p('<B><font color="008000" size=+2><I>Success!</I></font>');
  htp.para;
  htp.p('Designated record group updated successfully.');
END;


JavaScript code of to transfer data before running the mass update.

 

 
 
 
 
 
 
 
 
 
 
 

Figure 6

Feedback message from the mass update.

 

 
 
 
 
 
 
 
 
 
 
 

Room for improvement:


In this article we have really focused our effort on one technique to provide mass update to users: selection by query criteria. Most of the time users will ask for the ability to further refine the selection by checking or unchecking the records listed in the lower left frame in Figure 1. In order to provide this kind of functionality you would have to either use the pre-compiler created by the author or perform manual modifications to the code generated by Designer. Users may also request the ability designate manually a number of records to be updated by typing their ID code when they know them. That you can do with Designer only by using a technique similar to that shown in this article.

We could also have added a list of value against the monitor code in Figure 1. We will show how to add a list of value against such unbound item in a future article.
 

Hervé Deschamps is a Senior Principal Consultant with Oracle Corporation. He has had over five years of experience with Oracle Development Environments (Designer, Developer and previous versions). He has an extensive experience of all phases of the development life cycle with a focus in the areas of business analysis, data modeling and database design. He is also known in the technical community for his article publications centered around best custom development practices and for his user group presentations. You can reach him by e-mail at hdescham@us.oracle.com. He also maintains a web site full of articles, scripts, tips and techniques at http://www.iherve.com.