Introduction
To achieve the full benefits of ASP.NET (as opposed to ASP or PHP), one generally uses the traditional Server Control - ViewState - PostBack model to speed development time by automatically handling maintaining the state of the application between form submissions and allowing the developer to focus on functionality instead of wasting his time doing things such as repopulating form fields after a postback and writing messy inline code to do it.
However, this model has disadvantages when developing interface heavy applications using complex CSS and JavaScript, and becomes especially problematic when trying to use AJAX that is not part of ASP.NET AJAX - the problem stems from the fact that when using Server Controls (or even HTML controls set to runat=server
), the front-end code written by the developer often bears little resemblance to what is generated at the server. It also can be quite slow, especially when ASP.NET AJAX is introduced.
The library here presents an alternative, allowing the front end to be developed using straight HTML and / or JavaScript, using client side forms, while preserving the ability to maintain page state and to manipulate page content from server-side code without using a single inline tag. Furthermore, the problem of limited support for "Cross-Page PostBacks" has been solved - the target page that the user is sent to after form processing need not have anything to do with the submitting page.
Currently, the library, unmodified, has the following features and will work right out of the box:
- Maintains the state of INPUT (text, password, radio, checkbox, and hidden) and SELECT tags between POSTs and / or allows their state to be set from server-side code.
- Allows ordinary client side container elements (e.g.
DIV
s, SPAN
s) to be used as labels and/or panels, and to have their contents set on the server side.
- Allows the developer to specify arbitrary JavaScript to run on the target page after form processing is complete.
Using the Library
The library consists of a user control, FakePostBack.ascx, to be placed in the ASPX pages for which you wish to use the library, and the FormProcessor.cs class, to be placed in your app_code folder.
I've bundled a sample "calculator" app to demonstrate usage... Let us go through the example.
The Front-End Code (Demo.aspx)
<%@ Register Src = "FakePostBack.ascx" TagName="FormHelper" TagPrefix="sam" %>
<html>
<head>
</head>
<body onload="FormHelper.Populate();">
<sam:FormHelper runat="server" id="customForm"></sam:FormHelper>
<form name="calculate" method="post" action="Calculator.aspx">
<h2>Demo App using Form Processor.NET</h2>
<b>Enter an integer:</b><br />
<input type="text" id="num1" name="num1" /><br /><br />
<b>Enter another integer:</b><br />
<input type="text" id="num2" name="num2" /><br />
<input type="submit" value="Calculate Sum" />
</form>
<div id="sum"></div>
<script>
function SayHello()
{
alert("Thanks for using the calculator.");
}
</script>
</body>
</html>
This is an absolutely standard HTML page. All that has been done to include the front-end portion of the library is to add the FakePostBack.ascx control, and drop it in right about the form in question. The control requires no attributes rather than runat=server
. Also, the <body>
tag has been modified to run FormHelper.Populate
on load - this initializes the control.
The code-behind for the above Demo.aspx page is empty - it is not needed.
The Form-Processing Code (Calculator.aspx.cs)
This is a purely server-side page (Calculator.aspx is blank) and its job is to process the submitted form from Demo.aspx using our FormProcessor
class.
Let's look at what happens here:
FormProcessor p = new FormProcessor("Demo.aspx");
int a = Int32.Parse(p.Get("num1", "text"));
int b = Int32.Parse(p.Get("num2", "text"));
int c = a+b;
p.SetLabel("sum", "The sum is: "+c.ToString());
p.AddScript("SayHello();");
p.Finish();
An instance of FormProcessor
is created, specifying the page to load after processing has been completed - in this case, it is the submitting page - but it can be any page containing the client-side control.
Just a brief look at some relevant lines that will show you how to use the library:
p.Get("num1", "text")
This returns the contents of the "num1" input field - it is equivalent to using Request.Form["num1"]
but while maintaining state. The second argument of the Get
method specifies the type of form field (available values are "text
", "select
", "radio
", "checkbox
") - "text
" covers hidden and password fields as well.
p.SetLabel("sum", "The sum is: "+c.ToString());
This instructs the client-side module to populate the empty <div id="sum">
with the content specified.
p.AddScript("SayHello();");
The JavaScript specified here will be executed on the target page.
p.Finish()
The browser is redirected to Demo.aspx, the URL specified in the FormProcessor
constructor.
Page Before Submission
Page After Submission
How It Works
I won't go too much into detail here - the code in FormProcessor.cs, FakePostBack.ascx, and FacePostBack.ascx.cs are easily understandable, but briefly:
The FormProcessor.cs Get()
, SetLabel()
and AddScript()
methods add to a JSON array. FormProcessor.Finish()
places this array into a Session
variable, and redirects to the page containing FakePostBack.ascx.
The code-behind for FakePostBack.ascx checks for the presence of the Session
variable; if found, it is placed in a public string
and then set destroyed.
Finally, the call to FormHelper.Populate()
calls JavaScript generated by FakePostBack.ascx - it grabs the string
containing the JSON from the code-behind, and if the string
is not empty, iterates through the array, populating form fields, setting the content of elements, and executing any script blocks.
Acknowledgements
Sanford Liu, the brilliant product manager at Supernova Interactive, gave me the idea of using JSON as a way of keeping track of validation errors after form submission and then displaying them if errors had occurred.
Then, last night, at about 1 AM, after walking a beautiful young woman to the train station, I realized that I could use this concept to build a workable alternative to server-side forms in ASP.NET. And I did. :)
Enjoy!
History
- 12th December, 2008: Initial post