On a recent project we had a very complex Web application to
deliver in an environment where many of the end users had slow connections to
the server on which the application was to be deployed. As such, conservation
of bandwidth was one of the priorities. We decided to design for bandwidth
constraints from the beginning of the project. This focus led to a key
decision to develop the application primarily using client-side scripted
the proxy servers, etc. upstream of the end user, resulting in a significantly
smaller package needing to be retrieved from the main servers.
As this was an internal application for this organization,
we only had to consider our install base being Internet Explorer 5.5 or higher
the most suitable development choice.
This article and the corresponding example code are
available in a printable PDF download version.
Not all clear sailing
On the whole, this approach worked very well and allowed us
to have the application function faster than expected by the end users, who
were used to a heavily biased client/server approach even with their Web
applications. As most of the functionality was concerned with validation of
lines in total.
However, it was not all clear sailing, there were some
contained actual data rather than logic or functionality. It was these parts of
the data model which concerned us most as any change to the content would
require developer, rather than user, intervention. Some of the pseudo code for
the functions was similar to the following:
if ProductType=A then add items 1,2 & 3 to dropdown DD1
else if ProductType=B then add items 1, 4, 7 and 20 to dropdown DD1
else if Product Type=C then add null, 34, 78 to dropdown DD1
As you can see, while this is very simple to code up in a
it would not be in a format that an end user, or even a power user could be
left to update on his own. In any other design, we would have stored this
information in a database lookup table—such as the one shown in Listing B—and then interacted with it when we needed to.
We then asked the users about frequency of change of the
data concerned. If it did not change on a regular basis, then we could consider
leaving it in as shown in Listing A and take the hit of the development team
maintaining it. However, as this data was likely to change on a regular basis
and the client required immediate updates—rather than logging a support ticket
and waiting a few days—we decided to store it server side in a database table
with a maintenance screen for the key user to manage the data. We began to
discuss how we would pass this information from the server side to the client
to be processed; we considered several options including:
- A hidden IFrame on the page.
- An XML file.
- A new popup window.
We decided to use the hidden IFrame
approach so that we could hide the call from the users, but could also make the
IFrame visible for debugging if required.
Our next problem was how to convert the tabular database
table into a format that could be used by the rest of our application on the
client side. We very quickly decided to simply dump the table into two
Arrays—one for each column—and then cycle though them when required to populate
the drop-down box.
Just as we were on the verge of resigning ourselves to this
and preparing to brief the key users and management on this change and
performance hit, one of the consultants from SolarFish—one
of our consultancy firms—came up with an intriguing potential solution. He
realized that the information needed to be updated using server-side code, but
used on the client side. His suggestion was to amend the server-side script
that updated the database to recreate a client-side script file on the server,
which could then be loaded and cached like the rest. This file would then be as
static for its lifetime as any of the static files, rather than being created
dynamically every time it was called.
After some experimentation, we were able to get a working
prototype using classic ASP’s File
Scripting Object to create the file on the server as part of the update
process of the database table. The relevant fragment of code is shown in Listing C, and the result and output in
Listing D. As this file would only ever by updated
when the data was changed, we could treat it in the same way as the rest of our
client-side script code and it would also be cached.
Having proved that this approach would work, we re-examined
the rest of our Requirements Specification, and spotted several other
situations in which the same approach could be reused, either to ease
maintenance or to improve the offering we provided to our users. For example,
we were able to create a database table of the field labels on the screen and
then have any changes to the labels immediately populated to the end users’
screens in the same manner.
Using this approach, we were able to meet our internal goal
of delivering a system primarily using client-side code to minimize network
traffic for our customer. In addition, we were able to add several items of
functionality that could be managed by the customer rather than by a developer,
allowing a quicker response time to most simple change requests, such as label changes.
Also, as the code was automatically generated and the data directly
entered by the end users themselves, we were able to remove the developers—and
any typos, errors, delays, etc. that they could be blamed for—entirely from the
equation. This freed up the developers to work on other projects rather than
making minor changes to existing code every time these bits of information
Having created “code that wrote code” as we
described it to our Project Manager, we had found a method that could be reused
in other projects that we were working on. We had not come up with a name for
this approach, but it seemed to be some kind of code factory, although most of
the developers prefer to call these components “SkyNet” after the
computer AI in the Terminator films, or “Hal” after the AI in the
film 2001: A Space Odyssey; The
latter seemed appropriate primarily because bugs were hard to find and fix and
usually resulted in some really strange output.