Introduction
Every application at one point is challenged with a re-architecture to a more robust technology. While some are fortunate enough to rewrite their software entirely—updating the core language that the application is built on, most are tasked to do more with less. The approach that we took at Cayen Systems to give our app a face lift included utilizing JQuery with other client side technologies, while still preserving the classic ASP foundation.
Background
Our JQuery modernization can be described in two parts: the first is an overhaul of the UI, and the second is a refactoring of how the pages communicate with one another. I will not go into too much detail on how we modernized our code base using jQueryUI, since there are a plethora of available resources that can help a developer utilize this technology. Instead, the focus of this article will be on page architecture using jQuery and JSON with some exposure to our jQueryUI implementation.
As you read this article, if you have any questions about implementation, feel free to reference the demo project. All snippets referenced below can be found in the sandbox archive attached to the article.
Step 1: Create the JQuery Library File (module.js)
Within your module.js file, start by creating the placeholder for the JSON object. This variable will be global and will be initialized at a later point—to be explained. See the code below:
var jsonData = null;
Add code to the JQuery initializer function that will create a div & iframe in preparation for the jQuery dialog. I would like to point out that using iframes in some circles is considered poor practice, an alternative to using an iframe is to use AJAX coupled with dynamic html to populate the div. Since HTML 5 supports iframes, and this particular solution offers more isolation between the module and the implementation, I decided to go with this route instead:
$(function() {
$('body').append(
'<div id="divModule" style="padding:0;display:none;">' +
'<iframe id="iframeModule" style="width:100%;height:100%;"></iframe>' +
'</div>'
);
});
The openModule
function is responsible for setting the JSON object and opening the dialog. In order to make the module’s markup easier to manage, a separate HTML page is created that will house the interface. Once the openModule
function fires, the iframe’s location will update to the module.asp page; immediately thereafter the div is converted to a dialog:
function openModule(){
jsonData = loadModule();
$('#divModule').dialog({
height: 225,
width: 400,
title: 'Module Title',
modal: true,
resizeable: false,
close: function () {
closeModule();
},
open: function (event, ui) {
var url = 'Module.asp';
$('#iframeModule').get(0).src = url;
}
});
}
The closeModule
function is tasked with destroying the JSON object and closing the dialog. When the closeModule
function fires, the iframe’s location is set to a blank page—this is done to prevent a flicker when you reopen the dialog—and the JSON object is set to null:
function closeModule() {
jsonData = null;
if($('#divModule').is(':visible')) {
$('#divModule').dialog('close');
var url = 'about:blank';
$('#iframeModule').get(0).src = url;
}
}
The loadModule
function is responsible for receiving the JSON object from the server via AJAX. Despite the dataType being JSON, it is still necessary to eval the response:
function loadModule(){
var data = '';
data += 'Action=Load';
var json = $.ajax({
url: 'Module_Ajax.asp',
type: 'post',
async: false,
dataType: 'json',
data: data,
success: function (data) {
}
}).responseText;
return eval('(' + json + ')');
}
The saveModule function is tasked with pushing the JSON object back to the server via AJAX. JSON.stringify
converts the JSON object to a string format that the server can then convert back into a JSON object:
function saveModule() {
var data = '';
data += 'Action=Save';
data += '&JSON=' + JSON.stringify(jsonData);
return $.ajax({
url: 'Module_Ajax.asp',
type: 'post',
async: false,
dataType: 'text',
data: data
});
}
Step 2: Create the AJAX Page (module_ajax.asp)
Module_ajax.asp has several dependencies. One being json2.js, and the other being JSON_2.0.4.asp. The former allows the server to create JSON objects from strings and the latter allows the server to build JSON objects from another data source. It’s important to note that I’ve chosen to use the session as a construct to retrieve information, however in almost all cases you will want to use a database instead. Please tweak the database access code as necessary:
<%@Language="VBScript" CodePage="65001" %>
<%Option Explicit %>
<script language="JScript" runat="server" src=
<!-- #include file="include/JSON_2.0.4.asp" -->
<%
Dim Action
Action = Request("Action")
Select Case Action
Case "Load" Load()
Case "Save" Save()
Case Else Response.Write("Unknown Request")
End Select
The Load
function is responsible for creating the server side version of the JSON object, populating that object from a data source, and then converting the object into a string to be consumed by a client side script. Please update the data source from a session to what makes the most sense in your implementation:
Sub Load()
Dim ID
Dim JSONData
Set JSONData = jsObject()
JSONData("firstname") = Session("FirstName")
JSONData("lastname") = Session("LastName")
Response.Write(JSONData.jsString())
End Sub
The Save function is responsible for converting a JSON string into a server side JSON object and pushing the object’s information into a data source. Please update the data source from a session to what makes the most sense in your implementation:
Sub Save()
Dim JSONData
Set JSONData = JSON.Parse(Request.Form("JSON"))
Session("FirstName") = JSONData.firstname
Session("LastName") = JSONData.lastname
End Sub
%>
Step 3: Create the Module UI (module.asp)
The module itself should be relatively simply. Since it does not contain either the load or save functions, in most cases it will consist of little to no server side code. One thing to note, in the example below I chose to directly access the JSON object from the parent. I went with this approach because of its simplicity, however in some cases it might make more sense to move the JSON object between pages via querystring.
Since the module is a separate page isolated in an iframe, it needs to contain all of the javascript resources necessary for it to function. In addition to the typical JQuery resources (jquery.js and jquery-ui.js) we are also using pure.js. This file allows us to map the JSON object to particular fields on the page:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head>
<title>Sample Page</title>
<link type="text/css" rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/jqueryui/1.8/themes/base/jquery.ui.all.css" />
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.18/jquery-ui.js"></script>
<script type="text/javascript" src="http://beebole.com/pure/wp-content/themes/BeeBole-pure/libs/pure.js"></script>
In order to use directives you need to specify the UI element (using a JQuery selector) and the name of the JSON property. Once your directive is defined, you then call render which binds the JSON object to the UI fields:
<script type="text/javascript">
$(function () {
var directive = {
'#FirstName@value': 'firstname',
'#LastName@value': 'lastname'
};
$('form[name=Main]').render(window.parent.jsonData, directive);
});
The save function needs to map the values back to the JSON object. Unfortunately directives won’t help in this capacity, so to resolve this problem, we just write traditional JavaScript to update the JSON object. When ready just call the saveModule and closeModule functions as needed:
function save() {
window.parent.jsonData.firstname = $('#FirstName').val();
window.parent.jsonData.lastname = $('#LastName').val();
window.parent.saveModule();
window.parent.closeModule();
}
</script>
</head>
Next step is to create the fields necessary for displaying the data. It’s important to note that this interface will not post. Instead a save function needs to be called which will trigger the AJAX save (as defined in the module.js file):
<body>
<form name="Main">
<h1>Sample Module</h1><hr />
<label>First Name</label><input type="text" id="FirstName" /><br />
<label>Last Name</label><input type="text" id="LastName" /><br />
<input type="button" value="Save" onclick="save();" />
</form>
</body>
</html>
Step 4: Implement the Module (implementation.asp)
Since the module is self-sufficient, meaning it can work regardless of how it’s implemented, the implementation is extremely simple, just reference the module.js file in addition to any necessary JQuery dependencies and call the openModule function. Voilà, the module is completed:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head>
<title>Sample Page</title>
<link rel="Stylesheet" href="https://ajax.googleapis.com/ajax/libs/jqueryui/1.8/themes/base/jquery.ui.all.css" type="text/css" />
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.18/jquery-ui.js"></script>
<script type="text/javascript" src="javascript/json2.js"></script>
<script type="text/javascript" src="javascript/module.js"></script>
<script type="text/javascript">
$(function () {
$("input[name=OpenModule]").click(function () {
openModule();
});
});
</script>
</head>
<body>
<form name="Main" method="post">
<h1>Sample Module Implementation</h1>
<input type="button" name="OpenModule" value="Open Module" />
</form>
</body>
</html>
Points of Interest
The coolest part of this design is the isolation of the legacy code. With the exception of the AJAX page, all the code in this design is written with current industry standards, making it easier for the code to transition to newer technology—such as MVC & Razor.
History
No changes as of yet.