In Using Classes with VBScript 5.0, Eric Lippert writes, “Encapsulation and classes are not new concepts, but developers using VBScript have long gone without.” Since the Songhay System technology plan has been tied to Microsoft products since Access 2.0, I can ruefully detail what its ASP-based enterprise has “gone without”:
Anyone with basic ASP skills can easily find a solution to the Access Report and Form problems (if we relax our requirements for a solution that is generic, scaleable and portable). The last problem, however, takes some doing.
The opinion here is that the first two problems depend on the solution to the Datasheet Problem. Both the Report and Form problems are less generic than the Datasheet problem. In a scenario that is somewhat friendly to a generic solution, the user wants to search for data and get back a search result. The user would like to browse through the search results and edit them if need be. In the terms written here, this means Form-Datasheet-Form: the user searches with a form, gets back a search result in a Datasheet and handles the data in another form.
This implies that everything depends on the solution to the Datasheet problem. The solution must respond to these user demands:
And the solution must solve these developer problems:
All of these demands and problems have been grossly simplified and limited to protect the innocent. However, I think we have enough to go on for quite a few pages of text!
In Ad Hoc Web Reporting with ADO 2.0, Johnny Papa and Charles Caison developed (way back in 1998) an excellent solution to this Datasheet problem. But unfortunately they write, “What makes this even faster is that we only query the database once. That’s right, we create the ADO recordset once, and store it in an ASP Session variable.” By storing the ADO Recordset in a Session variable they are not satisfying my need for a scalable solution.
Randall Kindig’s Building a Custom Data Grid: Performing Ad Hoc Web Reporting with a VBScript 5.0 Class Object, basically gives me the solution to my Datasheet problem in a hot, Seattle coffee cup. But even in July of 2000 we still find him writing, “Since the recordset has been saved in a Session object, however, returning to the list causes the grid to simply pull up the saved recordset.”
In my struggle to find a stateless solution, I ran across the Microsoft Knowledge Base article Q202125 (HOWTO: Page Through a Recordset from ASP). This article reminds us that ADO Recordsets have the PageSize, PageCount, and AbsolutePage properties. This information addresses the need to be scaleable: to not use the Session variable!
Based on the aforementioned research, the solution to the Datasheet problem has two components: a reusable, portable, generic class called clsHTMLTable and an ASP page that “hosts” the class. The hosting ASP page would be “hard coded” to the specific solution but it is still worth mentioning here to illustrate how clsHTMLTable can be implemented. Before we do that, let’s examine clsHTMLTable:
Properties of clsHTMLTable | |
|---|---|
Recordset | Get/Set a disconnected, ADO Recordset (CursorType:=adOpenStatic and LockType:=adLockBatchOptimistic). The OLEDB provider must support the Recordcount, PageSize, PageCount, and AbsolutePage properties and the MoveFirst method of the ADODB library. |
URI | Get/Set a String literal that is an absolute or relative Uniform Resource Indicator. An instance of the class will render this URI in HTML using the first column of the ADO Recordset to form IIS QueryString objects. For example, if URI is set to <A HREF="./MyDataTable.asp?Get=CUST001">One Co.</A> <A HREF="./MyDataTable.asp?Get=CUST002">Two Co.</A> <A HREF="./MyDataTable.asp?Get=CUST003">3 Co.</A> … Where |
Width | Get/Set the WIDTH attribute of the TABLE tag. This is a String literal. By default it is set to 512. To set percentage values use double quotes. For example, to set 100% use ""100%"". |
Border | Get/Set the BORDER attribute of the TABLE tag. This is a number. By default it is set to 0. |
Padding | Get/Set the PADDING attribute of the TABLE tag. This is a number. By default it is set to 0. |
Spacing | Get/Set the SPACING attribute of the TABLE tag. This is a number. By default it is set to 0. |
RowHeadStyle | Get/Set the STYLE attribute of the TH tags. This is a String literal. By default it is set to "STYLE=""background: #FFFFFF none no-repeat scroll 0% 0%;""". |
Row1Style | Get/Set the STYLE attribute of the TD tags in odd-number rows. This is a String literal. By default it is set to "STYLE=""background: #FFFFFF none no-repeat scroll 0% 0%;""". |
Row2Style | Get/Set the STYLE attribute of the TD tags in even-number rows. This is a String literal. By default it is set to "STYLE=""background: #FFFFFF none no-repeat scroll 0% 0%;""". |
ColumnsArray | Get/Set an array of numbers. These numbers specify the column numbers that are used by the Recordset object to render the HTML table. Use this property if you have no control over the DBMS generating the ADO Recordset. By default it is set to vbNullString. |
FirstColAlignment | Get/Set the ALIGN attribute of the TD tags rendering the first column of the HTML table. This is a String literal. By default it is set to left. |
FirstColHeadAlignment | Get/Set the ALIGN attribute of the TH tags rendering the first column heading of the HTML table. This is a String literal. By default it is set to center. |
FirstColWidth | Get/Set the WIDTH attribute of the tags rendering the first column heading of the HTML table. This is a String literal. By default it is set to 0 (disabled). |
AbsolutePage | Get/Set the AbsolutePage property of the Recordset contained in an instance of the class. This is a number. By default it is set to 1. |
Pageable | Get/Set a Boolean value that determines if the Recordset will be “pageable”. When this is set to False the entire recordset is rendered in HTML. |
PageCount | Get the PageCount property of the Recordset contained in an instance of the class. This is a number. This property is read only. |
PageSize | Get/Set the PageSize property of the Recordset contained in an instance of the class. This is a number. By default it is set to 10. |
Methods of clsHTMLTable | |
|---|---|
Class_Initialize() | Sets default values for Private members. |
Class_Terminate() | Kills the Recordset contained in an instance of the class. |
GetTable() | This function returns an HTML TABLE block based on a “pageable” ADO Recordset object. The ColumnsArray property is used to “cherry pick” only the columns needed from the Recordset. When a URI is present, Column 0 of the Recordset is used as a QueryString and is not rendered in HTML. |
GetFlipTable() | This function returns an HTML TABLE block based on a “pageable” ADO Recordset object. When a URI is present, Column 1 of the Recordset is used as a QueryString Item and is not rendered in HTML. The recordset is flipped such that the leftmost column (Column 0) becomes the column headings. This is a clockwise motion. |
Here is a code sample:
'Get table. Set objHTMLTable = New clsHTMLTable Set objHTMLTable.Recordset = objADOrst vHTML = objHTMLTable.GetTable Response.Write vHTML Set objHTMLTable = Nothing objADOrst.Close Set objADO = Nothing
I hope this helps.
The design of the class should address this issue. Also note that the class depends heavily on CSS and W3C-compliant HTML. (The Songhay System DTD at the moment is HTML 4.0 Transitional.)
By making sure that this solution is stateless, we have ensured that it is scaleable. My only concerns are any performance issues surrounding how ADO creates a Recordset with its AbsolutePage property set. I think that ADO retrieves the records first and then applies the AbsolutePage property. This may mean performance problems during very large queries. I would defer to the tools in the DBMS to solve this problem (specifically stored procedures).
Now that I have expressed this historically Microsoft solution as a Class, it is portable. I don’t just mean that it can be translated into a COM object via Visual Basic. I am really concerned about leaving the Microsoft platform altogether—and taking my tools with me. I am talking about what Microsoft may consider unthinkable: translating VB Classes into, say, Java Classes! Gasp!
In this specific case, when shopping for a new platform to run my enterprise, this new technology must implement the equivalent of the Recordset object and the ability to page through its dataset. Additionally, the new web server will have to have something like the IIS QueryString object. (Of course the new platform must have its rendition of a Class!) These are the only vendor-specific dependencies I can find at the moment. By trying hard to not just make the code generic but to also make the code as platform independent as possible I make the solution portable.
By the way, using ASP server-side controls (from ASP+ and the new .NET framework) in my solutions would make them extremely platform dependent.
We have seen the star of the show, clsHTMLTable, now it’s time to explore its implementation. The table below lists the files of a simple application that handles data from Songhay System’s Funky Knowledge Base:
| Data Access Solution for The Funky Knowledge Base | |
|---|---|
default.asp | This is the root web file of the solution. This file displays a form. The form gathers the information needed to search The Funky Knowledge Base. It passes this information to This file is designed to scale up and host additional search forms. |
dataServices.asp | This is an include file that is “Hard Coded” to the DBMS used by The Funky Knowledge Base. In an effort to keep This file is designed to scale up and serve additional search forms built in to |
dataTable.asp | This file needs SQL parameters and a URI in order to run to completion. It drives a stored procedure to search for data based on a finite set of unknowns. A successful run renders the columns projected by the stored procedure in an HTML table. (The formatting for this table is “hard-coded” into this file.) The HTML page contains a form that recursively submits back to This file is designed to scale up and serve additional data entry/edit forms built like |
dataKB.asp | This page is accessed from the URIs in By reading the |
root.css | CSS style sheet. |
root.js | External JavaScript file that handles platform-neutral data binding and form validation. This file is designed to scale up and serve additional data entry/edit forms built like |
This is what The Funky KB “search form” looks like:

This is what some search results look like:

Finally, we have the form:

One major feature missing from my current build is the ability to sort by column on the fly. I personally do not have much call for it. I would rather have the user specify a sort during the initial search. The research material mentioned in this article supply quite enough information needed to implement this requirement.
Another major “user-friendly” feature missing is handling of pattern matching. Currently, wildcard searches depend on the user’s knowledge of the pattern matching characters of the DBMS. What is friendlier is to add Exact Phrase, Starts with, Ends with and/or Contains options in the initial search screen.
Finally, to address the performance issue, more research into implementing the Item “Initial Fetch Size” in the Properties Collection of the Recordset object and the use of adAsyncFetchNonBlocking, is needed. These features may not work with all OLEDB Providers under ADO. Moreover, once these features are in use, via a supporting Provider, there are miscellaneous limitations that must be addressed. For example Kindig writes, “Asynchronous fetches can obviously only be used with client-side cursors. However, they can really make it seem to the user as if data retrieval is happening faster. They can page through records and even click on hyperlinked fields without having to wait for all records to download. The downside is that if the records are to be sorted, the command will not complete until all records are downloaded.”