By Craig Pelkie
Synopsis: Although you can create functional Net.Data queries with just a few lines of code, there may be times where you need more control over the resulting HTML output. Using the Net.Data table functions, you can have complete control over HTML table rows and table column data. This article demonstrates the alternatives and describes some typical uses of the Net.Data table functions.
One of the first options you’ll probably explore when you start Web programming is Net.Data. Net.Data is provided with IBM HTTP Server for AS/400, which is a no-charge feature for the AS/400 system, so the entry price is right. Best of all, you can create simple Net.Data queries using just a few lines of code. For example, the code shown in Figure 1 is a small Net.Data macro that queries the QCUSTCDT database file. The resulting HTML table output, shown in Figure 2, is generated entirely by Net.Data. Because of this simplicity and power, I sometimes refer to Net.Data as “DFU for the Web.”
%{-------------------------------------------------------------%}
%{
Table1.ndm -- Net.Data table processing, example 1 %}
%{
Use Net.Data default table processing to format HTML. %}
%{
%}
%{
Copyright (c) 2000, Craig Pelkie %}
%{
ALL RIGHTS RESERVED %}
%{-------------------------------------------------------------%}
%{-------------------------------------------------------------%}
%{
Define section for the macro %}
%{-------------------------------------------------------------%}
%define
{
DATABASE =
"*LOCAL"
DTW_HTML_TABLE = "YES"
%}
%{-------------------------------------------------------------%}
%{
RUNSQL- run the SQL statement, generate default report %}
%{-------------------------------------------------------------%}
%function(DTW_SQL)
RUNSQL() {
select * from qiws.qcustcdt order by
cusnum
%}
%{-------------------------------------------------------------%}
%{
INPUT - initial section called, calls RUNSQL macro function %}
%{-------------------------------------------------------------%}
%html(INPUT)
{
<html>
<head>
<title>Net.Data macro
Table1.ndm</title>
</head>
<body>
<center>
<h1>Net.Data macro
Table1.ndm</h1>
@RUNSQL()
</center>
</body>
</html>
%}
Figure 1: Net.Data macro Table1.ndm.

Figure 2: The output of macro Table1.ndm.
To review, the Net.Data macro Table1.ndm shown in Figure 1 starts processing at the %html(INPUT) section (you can see the URL used to launch the macro in the address location in Figure 1). The INPUT section is mostly HTML code, with a call to the RUNSQL function, which is defined as a Net.Data DTW_SQL function type. That function includes the SQL SELECT statement. Because there are no additional options specified for working with the result set, Net.Data generates the default table output. The DTW_HTML_TABLE option in the %define section causes Net.Data to use HTML table tags, rather than the default <PRE> (preformatted) tag. If you run the macro, you should remove the DTW_HTML_TABLE tag to see how the table is displayed. You will undoubtedly remember to always include the DTW_HTML_TABLE tag after you see the alternative.
You can use the Table1.ndm macro as a ready-to-use template for any database file on your AS/400 system. By simply changing the SQL statement, you can quickly query any database file and use Net.Data to generate an HTML table.
Although the Table1.ndm macro is good for “quick and dirty” applications and prototypes, chances are that you will want to create better looking Web applications. For example, Figure 3 shows the output of another version of the macro, Table2.ndm, with several visual enhancements:

Figure 3: Table2.ndm output is more refined.
The code for the Table2.ndm is shown in Figure 4. In the %define section, I have removed the DTW_HTML_TABLE statement, since I will be creating the HTML Table. The %html(INPUT) section is the same as the Table1.ndm macro, but the RUNSQL function is much more involved.
%{-------------------------------------------------------------%}
%{
Table2.ndm -- Net.Data table processing, example 2 %}
%{
Use %report block in SQL function for more control. %}
%{ %}
%{
Copyright (c) 2000, Craig Pelkie %}
%{
ALL RIGHTS RESERVED %}
%{-------------------------------------------------------------%}
%{-------------------------------------------------------------%}
%{
Define section for the macro %}
%{-------------------------------------------------------------%}
%define
{
DATABASE = "*LOCAL"
%}
%{-------------------------------------------------------------%}
%{
RUNSQL- run the SQL statement, generate default report %}
%{-------------------------------------------------------------%}
%function(DTW_SQL)
RUNSQL() {
select * from qiws.qcustcdt order by
cusnum
%report{
<!---------------------------------------------------
Table headers
---------------------------------------------------->
<br>
<table border="0"
cellpadding="1"
bgcolor="lightcyan">
<tr bgcolor="lightsteelblue">
<th><font
size="-2">$(N1 )<br>Customer Number</font></th>
<th><font
size="-2">$(N2 )<br>Last Name </font></th>
<th><font
size="-2">$(N3 )<br>Initials </font></th>
<th><font
size="-2">$(N4 )<br>Street </font></th>
<th><font
size="-2">$(N5 )<br>City </font></th>
<th><font
size="-2">$(N6 )<br>State </font></th>
<th><font
size="-2">$(N7 )<br>Zip Code </font></th>
<th><font
size="-2">$(N8 )<br>Credit Limit </font></th>
<th><font
size="-2">$(N9 )<br>Charge Code </font></th>
<th><font
size="-2">$(N10)<br>Balance Due </font></th>
<th><font
size="-2">$(N11)<br>Credit Due </font></th>
</tr>
<!---------------------------------------------------
Table rows
---------------------------------------------------->
%row{
<tr>
<td ><font
size="-2">$(V1 )</font></td>
<td ><font
size="-2">$(V2 )</font></td>
<td ><font size="-2">$(V3
)</font></td>
<td ><font
size="-2">$(V4 )</font></td>
<td ><font
size="-2">$(V5 )</font></td>
<td ><font
size="-2">$(V6 )</font></td>
<td ><font
size="-2">$(V7 )</font></td>
<td
align="right" ><font size="-2">$(V8
)</font></td>
<td
align="center"><font size="-2">$(V9
)</font></td>
<td
align="right" ><font
size="-2">$(V10)</font></td>
<td
align="right" ><font
size="-2">$(V11)</font></td>
</tr>
%}
</table>
%}
%}
%{-------------------------------------------------------------%}
%{
INPUT - initial section called, calls RUNSQL macro function %}
%{-------------------------------------------------------------%}
%html(INPUT)
{
<html>
<head>
<title>Net.Data macro
Table2.ndm</title>
</head>
<body>
<center>
<h1>Net.Data macro Table2.ndm</h1>
@RUNSQL()
</center>
</body>
</html>
%}
Figure 4: Net.Data macro Table2.ndm.
The RUNSQL function now includes a Net.Data %report block, immediately following the SQL statement itself. When Net.Data encounters a %report block, it assumes that you want to control the HTML generation, so it does not generate the default HTML table. There are two sections to a %report block:
The table headers section includes the HTML needed to define a table and the table row for the table headers. Within the table row, there are table header tags for each of the column headers. The Net.Data table variable $(Nx) (where “x” is a number from 1 to 11) is used to retrieve the name of the column, in position order as defined in the result set. The table header tag also includes an HTML break tag <br> and the text of the column header.
The %row block is applied to each of the rows returned from the SQL statement. Note that you do not have to define a looping construct; Net.Data provides an automatic loop over each of the result set rows. The code within the %row block is applied to each of the result set rows.
Within the %row block, the <tr> tag defines the table row, with <td> tags for each of the table data elements. The value of each element is referenced by the Net.Data variable $(Vx), where “x” is a number from 1 to 11. An alternative to the $(Vx) table variable is $(V_name), where _name is the actual column name from the database. For example, you can refer to the value of the columns returned as $(V_cusnum), $(V_lstnam), etc. Although it is simpler to use the $(Vx) format, you might prefer to use the $(V_name) format, since it is more descriptive.
Although you have more control of the HTML table generation with the %report block technique, the processing for each row is the same. If you need to perform unique operations on each row, you need to use the next technique.
Figure 5 shows the output of the Table3.ndm macro. You can see that I have applied a different background color to each table row, which creates a more striking visual appearance. Figure 6 shows a further enhancement to the table, in which the customer number column is now used as a link to a customer detail Web page. To get this level of control, where you can apply specific formatting to individual rows and cells, you need to use Net.Data table functions.

Figure 5: Complete control in Table3.ndm.

Figure 6: The table now has a clickable link.
Figure 7 shows part of the code for the Table3.ndm macro that generated the output shown in Figure 5. There are many differences between this macro and the previous two macros:
%{----------------------------------------------------------------------------%}
%{
Table3.ndm -- Net.Data table processing, example 3 %}
%{
Use Net.Data table variables for complete control of table display. %}
%{ %}
%{
Copyright (c) 2000, Craig Pelkie %}
%{
ALL RIGHTS RESERVED %}
%{----------------------------------------------------------------------------%}
%{----------------------------------------------------------------------------%}
%{
Define section for the macro %}
%{----------------------------------------------------------------------------%}
%define
{
DATABASE =
"*LOCAL"
DTW_DEFAULT_REPORT = "NO"
sqlTable =
%TABLE
%}
%{----------------------------------------------------------------------------%}
%{
RUNSQL - run the SQL statement, return %TABLE variable %}
%{----------------------------------------------------------------------------%}
%function(DTW_SQL)
RUNSQL(OUT sqlTable) {
select * from qiws.qcustcdt order by
cusnum
%}
%{----------------------------------------------------------------------------%}
%{
OUTPUT_TABLE - macro function to work with %TABLE variable %}
%{----------------------------------------------------------------------------%}
%macro_function
OUTPUT_TABLE (IN sqlTable) {
<!---------------------------------------------------
Table headers
---------------------------------------------------->
<br>
<table border="0"
cellpadding="1"
bgcolor="lightcyan">
<tr bgcolor="lightsteelblue">
<th><font
size="-2">
@dtw_tb_rgetn(sqlTable,
"1")<br>Customer Number
</font></th>
<th><font
size="-2">
@dtw_tb_rgetn(sqlTable,
"2")<br>Last Name
</font></th>
<th><font
size="-2">
@dtw_tb_rgetn(sqlTable,
"3")<br>Initials
</font></th>
<th><font
size="-2">
@dtw_tb_rgetn(sqlTable,
"4")<br>Street
</font></th>
<th><font
size="-2">
@dtw_tb_rgetn(sqlTable,
"5")<br>City
</font></th>
<th><font
size="-2">
@dtw_tb_rgetn(sqlTable,
"6")<br>State
</font></th>
<th><font
size="-2">
@dtw_tb_rgetn(sqlTable,
"7")<br>Zip Code
</font></th>
<th><font
size="-2">
@dtw_tb_rgetn(sqlTable,
"8")<br>Credit Limit
</font></th>
<th><font
size="-2">
@dtw_tb_rgetn(sqlTable,
"9")<br>Charge Code
</font></th>
<th><font size="-2">
@dtw_tb_rgetn(sqlTable,
"10")<br>Balance Due
</font></th>
<th><font
size="-2">
@dtw_tb_rgetn(sqlTable,
"11")<br>Credit Due
</font></th>
</tr>
<!---------------------------------------------------
Table rows
---------------------------------------------------->
@dtw_tb_rows(sqlTable, numRows)
@dtw_assign(rowIndex, "1")
@dtw_assign(color,
"lightcyan")
%while (rowIndex <= numRows) {
<tr bgcolor=$(color)>
<td
align="center"><font size="-2">
@dtw_tb_rgetv(sqlTable,
rowIndex, "1")
</font></td>
<td><font
size="-2">
@dtw_tb_rgetv(sqlTable,
rowIndex, "2")
</font></td>
<td><font
size="-2">
@dtw_tb_rgetv(sqlTable,
rowIndex, "3")
</font></td>
<td><font
size="-2">
@dtw_tb_rgetv(sqlTable,
rowIndex, "4")
</font></td>
<td><font
size="-2">
@dtw_tb_rgetv(sqlTable,
rowIndex, "5")
</font></td>
<td><font
size="-2">
@dtw_tb_rgetv(sqlTable,
rowIndex, "6")
</font></td>
<td><font
size="-2">
@dtw_tb_rgetv(sqlTable,
rowIndex, "7")
</font></td>
<td
align="right"><font size="-2">
@dtw_tb_rgetv(sqlTable,
rowIndex, "8")
</font></td>
<td
align="center"><font size="-2">
@dtw_tb_rgetv(sqlTable,
rowIndex, "9")
</font></td>
<td
align="right"><font size="-2">
@dtw_tb_rgetv(sqlTable,
rowIndex, "10")
</font></td>
<td
align="right"><font size="-2">
@dtw_tb_rgetv(sqlTable,
rowIndex, "11")
</font></td>
</tr>
%if (color ==
"lightcyan")
@dtw_assign(color,
"silver")
%else
@dtw_assign(color,
"lightcyan")
%endif
@dtw_add(rowIndex, "1",
rowIndex)
%}
</table>
%}
%{----------------------------------------------------------------------------%}
%{
INPUT - initial section called, calls RUNSQL macro function %}
%{----------------------------------------------------------------------------%}
%html(INPUT)
{
<html>
<head>
<title>Net.Data macro
Table3.ndm</title>
</head>
<body>
<center>
<h1>Net.Data macro
Table3.ndm</h1>
@RUNSQL(sqlTable)
@OUTPUT_TABLE(sqlTable)
</center>
</body>
</html>
%}
Figure 7: Net.Data macro Table3.ndm.
In the %define section, I added the DTW_DEFAULT_REPORT statement to indicate to Net.Data that it is not supposed to generate an HTML report. I need that statement because the RUNSQL section now contains just an SQL statement, as in the Table1.ndm macro. If I didn’t have the DTW_DEFAULT_REPORT option set to “no”, Net.Data would assume that it should generate a report after executing the SQL statement.
I also defined the variable sqlTable in the %define section as a Net.Data %table type variable. This is a special variable type that contains the result set of an SQL SELECT statement. I need this variable so that I can pass the result set from the RUNSQL function to my macro function OUTPUT_TABLE. (In RPG terms, the %table type variable is conceptually similar to a multiple occurrence data structure. Unlike the data structure, I don’t need to declare subfields or an upper limit; Net.Data automatically fills the sqlTable variable with records retrieved from the database file.)
Look now at the %html(INPUT) section. The RUNSQL function call includes the sqlTable parameter, which is defined in the RUNSQL function as an output parameter. When Net.Data executes the RUNSQL function, it runs the SELECT statement, then returns the sqlTable variable. The OUTPUT_TABLE function invocation in the INPUT section uses sqlTable as an input parameter, so I am able to pass the results of the SQL statement to another function.
Why is it necessary to pass the sqlTable variable from RUNSQL to OUTPUT_TABLE, rather than just code the statements for OUTPUT_TABLE in a %report block following the SQL statement? The reason is because some of the Net.Data functions that I want to use are not supported in a %report block. The functions that are not supported are those that let me have access to individual column values in a row. Because I want access to individual column values (cells in the table), I have to create the OUTPUT_TABLE function, pass it the sqlTable variable, and perform my own processing of the table in that function.
The OUTPUT_TABLE function that is shown in Figure 7 is an abbreviated version of the actual function; much of the repetitive code for different column headings and values has been omitted. However, you can see the specific Net.Data functions that are used to work with individual column values.
Starting in the section labeled “Table headers”, I set up the table and the column headings. This time, instead of using the $(Nx) variables for column names, I need to use the Net.Data function @dtw_tb_rgetn (Table Get Name). The parameters for the function are the name of the table (sqlTable) and the column number whose name I want to retrieve.
Heads up: there is also a function @dtw_tb_getn to return the name of a column. The difference is that @dtw_tb_getn uses a third parameter to return the name of the column in a variable. Use that version of the function if you want to retrieve the column name and use it later. Since I am using the column name directly “in place”, I use the “return” form of the function, to return the value directly where it is used in the macro. Most Net.Data functions have both forms of a function (an “r” version and a non-“r” version). The documentation for all of the “r” versions is found in the non-“r” version in the Net.Data references.
After setting the table column headers, I work with the table rows. In this section, I need to perform my own loop over the sqlTable result set. I cannot use the Net.Data %row block to provide automatic looping. The first few statements in the “Table rows” section retrieve the number of rows in the sqlTable, assign a starting row index value, and set the background color for the first row. I then set up a while condition, to loop while the row index is less than or equal to the number of rows in the result set.
Inside the loop, I format an HTML table row for each row in the result set. To access the column values in the result set, I use the @dtw_tb_rgetv (Table Get Value) function. This function uses three parameters: the name of the table variable, the row within the table variable, which is incremented by one each time through the loop, and the column number within the row. On each pass through the while loop, all of the columns in a row are retrieved and formatted.
At the bottom of the loop (after the closing </tr> tag), I use an %if condition to flip the background color on alternating rows. I then increment the rowIndex variable and close the while loop.
Figure 6 shows the Table4.ndm macro output, with an <a href> link applied to the customer number column. When you click on a customer number, the macro displays a “detail” page, similar to Figure 8. The example shows how to have the Net.Data equivalent of a subfile going to a detail display.

Figure 8: The Customer Detail page.
Figure 9 shows the modification to the code from Table3.ndm that puts the link onto the customer number. Figure 9 also includes part of the %html(CUSTOMER_DETAIL) section of the Table4.ndm macro, which creates the customer detail page shown in Figure 8.
%{----------------------------------------------------------------------------%}
%{
Table4.ndm -- Net.Data table processing, example 4 %}
%{
Create link from customer number to display detail form. %}
%{
%}
%{
Copyright (c) 2000, Craig Pelkie
%}
%{
ALL RIGHTS RESERVED %}
%{----------------------------------------------------------------------------%}
%{----------------------------------------------------------------------------%}
%{
Define section for the macro %}
%{----------------------------------------------------------------------------%}
%define
{
DATABASE =
"*LOCAL"
DTW_DEFAULT_REPORT = "NO"
sqlTable =
%TABLE
%}
%{----------------------------------------------------------------------------%}
%{
RUNSQL - run the SQL statement, return %TABLE variable %}
%{----------------------------------------------------------------------------%}
%function(DTW_SQL)
RUNSQL(IN sqlString,
OUT sqlTable) {
$(sqlString)
%}
%{----------------------------------------------------------------------------%}
%{
OUTPUT_TABLE - macro function to work with %TABLE variable %}
%{----------------------------------------------------------------------------%}
%macro_function
OUTPUT_TABLE (IN sqlTable) {
<!---------------------------------------------------
Table headers
---------------------------------------------------->
<br>
<table border="0"
cellpadding="1"
bgcolor="lightcyan">
<tr bgcolor="lightsteelblue">
<th><font
size="-2">
@dtw_tb_rgetn(sqlTable,
"1")<br>Customer Number
</font></th>
<th><font
size="-2">
@dtw_tb_rgetn(sqlTable,
"2")<br>Last Name
</font></th>
<th><font
size="-2">
@dtw_tb_rgetn(sqlTable,
"3")<br>Initials
</font></th>
<th><font
size="-2">
@dtw_tb_rgetn(sqlTable,
"4")<br>Street
</font></th>
<th><font
size="-2">
@dtw_tb_rgetn(sqlTable,
"5")<br>City
</font></th>
<th><font
size="-2">
@dtw_tb_rgetn(sqlTable,
"6")<br>State
</font></th>
<th><font
size="-2">
@dtw_tb_rgetn(sqlTable,
"7")<br>Zip Code
</font></th>
<th><font
size="-2">
@dtw_tb_rgetn(sqlTable,
"8")<br>Credit Limit
</font></th>
<th><font
size="-2">
@dtw_tb_rgetn(sqlTable,
"9")<br>Charge Code
</font></th>
<th><font
size="-2">
@dtw_tb_rgetn(sqlTable,
"10")<br>Balance Due
</font></th>
<th><font
size="-2">
@dtw_tb_rgetn(sqlTable,
"11")<br>Credit Due
</font></th>
</tr>
<!---------------------------------------------------
Table rows
---------------------------------------------------->
@dtw_tb_rows(sqlTable, numRows)
@dtw_assign(rowIndex, "1")
@dtw_assign(color,
"lightcyan")
%while (rowIndex <= numRows) {
<tr bgcolor=$(color)>
@dtw_tb_getv(sqlTable,
rowIndex, "1", cusnum)
<td align="center"><font
size="-2">
<a
href="CUSTOMER_DETAIL?cusnum=$(cusnum)">$(cusnum)</a>
</font></td>
<td><font
size="-2">
@dtw_tb_rgetv(sqlTable,
rowIndex, "2")
</font></td>
<td><font
size="-2">
@dtw_tb_rgetv(sqlTable,
rowIndex, "3")
</font></td>
<td><font
size="-2">
@dtw_tb_rgetv(sqlTable,
rowIndex, "4")
</font></td>
<td><font
size="-2">
@dtw_tb_rgetv(sqlTable,
rowIndex, "5")
</font></td>
<td><font
size="-2">
@dtw_tb_rgetv(sqlTable,
rowIndex, "6")
</font></td>
<td><font
size="-2">
@dtw_tb_rgetv(sqlTable,
rowIndex, "7")
</font></td>
<td
align="right"><font size="-2">
@dtw_tb_rgetv(sqlTable,
rowIndex, "8")
</font></td>
<td
align="center"><font size="-2">
@dtw_tb_rgetv(sqlTable,
rowIndex, "9")
</font></td>
<td
align="right"><font size="-2">
@dtw_tb_rgetv(sqlTable,
rowIndex, "10")
</font></td>
<td
align="right"><font size="-2">
@dtw_tb_rgetv(sqlTable,
rowIndex, "11")
</font></td>
</tr>
%if (color ==
"lightcyan")
@dtw_assign(color,
"silver")
%else
@dtw_assign(color,
"lightcyan")
%endif
@dtw_add(rowIndex, "1",
rowIndex)
%}
</table>
%}
%{----------------------------------------------------------------------------%}
%{
INPUT - initial section called, calls RUNSQL macro function %}
%{----------------------------------------------------------------------------%}
%html(INPUT)
{
<html>
<head>
<title>Net.Data macro
Table4.ndm</title>
</head>
<body>
<center>
<h1>Net.Data macro
Table4.ndm</h1>
@dtw_assign(sqlString,
"select * from qiws.qcustcdt order by cusnum")
@RUNSQL(sqlString, sqlTable)
@OUTPUT_TABLE(sqlTable)
</center>
</body>
</html>
%}
%{----------------------------------------------------------------------------%}
%{
CUSTOMER_DETAIL - display detail record for a customer %}
%{----------------------------------------------------------------------------%}
%html(CUSTOMER_DETAIL)
{
%{------------------------------------------------------------------------%}
%{ Extract customer number from
QUERY_STRING environment variable
%}
%{------------------------------------------------------------------------%}
@dtw_assign(queryString,
@dtw_rgetenv("QUERY_STRING"))
@dtw_assign(eqPos, @dtw_rpos("=", queryString))
@dtw_assign(qsCusnum, @dtw_rsubstr(queryString, @dtw_radd(eqPos,
"1")))
@dtw_assign(sqlString, "select * from qiws.qcustcdt where
cusnum = $(qsCusnum)")
@RUNSQL(sqlString, sqlTable)
<head>
<title>Net.Data macro
Table4</title>
</head>
<body>
<center>
<h1>Detail for Customer</h1>
<!---------------------------------------------------------------------
Customer Detail table
---------------------------------------------------------------------->
<table bgcolor="lightcyan"
border="2"
cellpadding="3">
<!-------------------------------------------------------------
Table heading
-------------------------------------------------------------->
<tr>
<td colspan="2"
align="middle"
bgcolor="lightblue">
<font
color="navy">Customer Details</font>
</td>
</tr>
<!------------------------------------------------
Customer number
------------------------------------------------->
<tr>
<td width="40%"
bgcolor="linen">
<font
size="-2">Customer number</font>
</td>
<td>
<font
size="-2">@dtw_tb_rgetv(sqlTable, "1",
"1")</font>
</td>
</tr>
<!------------------------------------------------
Name (Initials
Lastname)
------------------------------------------------->
<tr>
<td bgcolor="linen">
<font
size="-2">Name</font>
</td>
<td>
<font
size="-2">@dtw_tb_rgetv(sqlTable, "1",
"3")
@dtw_tb_rgetv(sqlTable, "1",
"2")
</font>
</td>
</tr>
<!------------------------------------------------
Street
------------------------------------------------->
<tr>
<td bgcolor="linen">
<font
size="-2">Street</font>
</td>
<td>
<font
size="-2">@dtw_tb_rgetv(sqlTable, "1",
"4")</font>
</td>
</tr>
<!------------------------------------------------
City State Zipcode
------------------------------------------------->
<tr>
<td bgcolor="linen">
<font
size="-2">City, State Zip</font>
</td>
<td>
<font
size="-2">@dtw_tb_rgetv(sqlTable, "1", "5"),
@dtw_tb_rgetv(sqlTable,
"1", "6")
@dtw_tb_rgetv(sqlTable,
"1", "7")
</font>
</td>
</tr>
<!------------------------------------------------
Credit Limit
------------------------------------------------->
<tr>
<td
bgcolor="linen">
<font
size="-2">Credit Limit</font>
</td>
<td>
<font
size="-2">@dtw_tb_rgetv(sqlTable, "1",
"8")</font>
</td>
</tr>
<!----------------------------------------------------------------
"Back"
button
------------------------------------------------------------------> <tr
bgcolor="lightblue">
<td align="center"
colspan="2">
<input
type="button"
value="Return
to Query"
onClick="history.back()">
</td>
</tr>
</table>
</center>
</body>
%}
Figure 9: Net.Data macro Table4.ndm.
Now, it is true that you can attach a link to the customer number column when using the %report/%row blocks, as shown in the Table2.ndm macro (Figure 4). However, it is important to remember that in a %row block, the formatting is applied to all rows in the result set. Using the technique shown in Table3.ndm and Table4.ndm, where I explicitly control the looping over the result set, I can use conditional logic on each row. For example, I might code the macro so that it creates a link on the customer number only if the balance due is greater than zero. By using the @dtw_tb_rgetv function and coding my own loop, I can add that conditional logic and apply my formatting to just the particular rows that meet the criteria.
The actual assignment of the <a href> link uses the Net.Data @dtw_tb_getv function to assign the value of the customer number to the variable cusnum. Once I have that variable, I code the <a href> statement. The resulting HTML statement will look like this when sent to the browser:
<a href="CUSTOMER_DETAIL?cusnum=192837">192837</a>
The value of the <a href> points to the CUSTOMER_DETAIL section of my Net.Data macro. The part following the ? mark is available to my Net.Data macro in an environment variable named “query string”. What I have done is create a URL that will invoke the next section of my macro to display the Customer Details Web page, and provide the customer number that I want to work with to the macro.
In the CUSTOMER_DETAIL section, the first four functions are used to extract the value of the query string environment variable. The statements retrieve the value of the query string environment variable, find the position of the equal sign in the value, then extract everything to the right of the equal sign. That gives me the value of the customer number that I clicked, which I then use in the SELECT statement that I generate to retrieve data for just that customer.
I also modified the RUNSQL function so that it now accepts an input parameter, sqlString. By using the sqlString parameter, I can use the same RUNSQL function to retrieve the entire list of customers or an individual customer, just by varying the SELECT statement that is passed in sqlString. The results are still returned in the sqlTable variable. For an individual customer SELECT, the sqlTable variable is a result set containing just one row.
I use the @dtw_tb_rgetv function in the CUSTOMER_DETAIL section to retrieve the columns from the sqlTable variable, and format the values into the detail display.
At this point, you may be somewhat overwhelmed by Net.Data’s table handling features. It took me some time to understand how the different functions are used. I categorize the features as shown in Figure 10. As with most programming, you need to write more code to have greater control. For applications where you don’t need complete access to a Net.Data table, you should use the automatic Net.Data table generation or the simple %report/%row block constructs. As shown in Figure 6, you can create some nice looking tables in Net.Data with great functionality, but you will have to write considerably more HTML and Net.Data code than is required with the other techniques.
|
Feature |
Ease of Use |
Description |
When to Use |
|
Automatic table generation |
Easy |
Provides automatic HTML table to display records from an SQL query. |
Quick-and-dirty, one time applications, prototypes. |
|
Report blocks |
Moderate |
Lets you customize table headings, background colors, apply formatting to table cells. |
When you need more control over the appearance of an HTML table, but do not need access to individual elements within the HTML table. |
|
@dtw_tb_getn, @dtw_tb_getv |
Difficult |
Provides complete access to and control over individual cells in an HTML table. |
When you need to format individual cells in an HTML table, depending upon conditions in the row data. |
Figure 10: Summary of Net.Data table features.
Note: the complete code for the four macros described in this article, Table1.ndm through Table4.ndm, is freely available at www.web400.com.
Craig Pelkie works for a dot.com startup company in the San Diego, CA. area. In addition to conducting AS/400 related seminars, he works with IBM Learning Services as an instructor for course S6178, “Using Net.Data with the AS/400”, which he developed. He is available for programming consulting for interesting projects, and may be contacted at craig@web400.com.