The ASP.NET Diet Part 1 – Making a Thin Contact Form
In my last post I waxed philosophically about the progression of ASP.NET. Today I want to start actually demonstrating how to take ASP.NET to the next level, at least in my opinion. The first thing I always add to a new web site is a contact form. Basically the form accepts a question or comment from a visitor and allows them to add their contact information to the request. The way I implement the form is to display a visual confirmation to the visitor, store the request in the site’s database, send an E-Mail to the visitor to confirm the request and send an E-Mail to the site administrator to alert them. Traditionally this was done with a post-back to the server, which stored the information and sent the E-Mails. The confirmation was displayed in response to the post-back. Since AJAX has become popular this process has evolved.
First let’s examine implementing this form with an UpdatePanel and a MultiView control. I am not going to deal with the E-Mail or database activities because they do not really matter for this demonstration. The main thing is to see how to quickly add AJAX to the form. The UpdatePanel wraps a MultiView that is composed of two views, one for the form and one for the confirmation message.
<asp:UpdatePanel runat="server" ID="upContact">
<ContentTemplate>
<asp:MultiView ID="MultiView1" runat="server" ActiveViewIndex="0">
<asp:View runat="server" ID="vForm">
<table cellspacing="1" cellpadding="1" width="92%" align="center" border="0">
<tr>
<td height="80" valign="top" align="left" colspan="3">
<h2 style="margin-top: 4px; margin-left: 5px; margin-right: 5px; text-align: center;">
Contact Us Via FAT AJAX</h2>
</td>
</tr>
<tr>
<td colspan="3">
<!-- Contact Form Elements Using Web Controls -->
</td>
</tr>
</table>
<p align="center">
<asp:Button ID="btnSubmit" runat="server" Text="Submit" /><br>
</p>
</asp:View>
<asp:View runat="server" ID="vThanks">
<!-- Confirmation Message -->
</asp:View>
</asp:MultiView>
</ContentTemplate>
</asp:UpdatePanel>
When the form is submitted the actions described above occur and the confirmation View is displayed. With the UpdatePanel in place this happens without a flicker happening on the screen, which is desirable. You can even add an UpdateProgress control to give a little visual indication while the data is stored and E-Mails sent.
Protected Sub btnSubmit_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnSubmit.Click
'Do Work Here
MultiView1.ActiveViewIndex = 1
End Sub
Nothing really magically with this code, but I like to see what is going on under the hood. For this I crack open Fiddler. Monitoring the contact request with Fiddler shows it sends and receives a total of 2385 bytes. You can check out exactly what what was sent across the wire in both the request and the response. This will be important to remember later.
The UpdatePanel solution is sufficient in most cases. It is fast to market, provides a rich user experience and does the work needed. But in the public arena this may not cut it. I mean it is a very competitive world these days for market share and the leaner the better (well typically), but this often requires just a little more work. The next example makes a really thin Contact form, but requires a bit more work to make happen.
I am going to continue to use the ScriptManager control to make leveraging ASP.NET AJAX a lot easier. Instead of using Web Controls for inputs I am going to use the HTML Input tags. I like using the actual HTML Input tags over Web Controls because they are much cleaner to reference in JavaScript. Instead of an UpdatePanel I am going to use a Web Service and a simple JavaScript file. The Web Service and the script files are added to the ScriptManager as shown below. This is important, otherwise the form will not function.
<asp:ScriptManager ID="ScriptManager1" runat="server" LoadScriptsBeforeUI="false">
<Services>
<asp:ServiceReference Path="~/ContactService.asmx" />
</Services>
<Scripts>
<asp:ScriptReference Path="~/js/Contact.js" />
</Scripts>
</asp:ScriptManager>
<fieldset id="contactform">
<legend>Contact Us</legend>
<div id="dMakeContact">
<!-- Contact Form -->
</div>
<div id="dThinking">
Thinking....</div>
<div id="ContactResponse">
<p>
Your request has been recieved and will be responded to shortly.</p>
</div>
</fieldset>
Notice there is no MultiView here either, instead there are a series of DIV elements. The dThinking and ContactResponse DIVs are hidden at load time by the JavaScript. This can also be done by setting the styles applied to these elements to hidden, but I wanted to keep CSS out of this post.
var dThinking = $get('dThinking');
var ContactResponse = $get('ContactResponse');
if (ContactResponse != null) {
HideElement('ContactResponse');
HideElement('dThinking');
}
There is no code-behind handling the click event of the form’s submit button, instead the request is sent to a web service that performs the business end of the form.
Imports System.Web
Imports System.Web.Services
Imports System.Web.Services.Protocols
Imports System.Web.Script.Services
<WebService(Namespace:="http://tempuri.org/")> _
<WebServiceBinding(ConformsTo:=WsiProfiles.BasicProfile1_1)> _
<ScriptService()> _
<Global.Microsoft.VisualBasic.CompilerServices.DesignerGenerated()> _
Public Class ContactService
Inherits System.Web.Services.WebService
<WebMethod()> _
<ScriptMethod()> _
Public Function PostContactRequest(ByVal vRequest As ContactInfo) As Boolean
Dim ret As Boolean = False
'Simulate doing some real work here
Threading.Thread.Sleep(1000)
ret = True
Return ret
End Function
End Class
Notice three modifications to this web service, the importing of the System.Web.Script.Services namespace and the addition of the ScriptService and ScriptMethod attributes to the class and method respectively. This makes the web service method work with ASP.NET AJAX by automatically using JSON to transmit data. JSON stands for JavaScript Object Notation and is a much nicer way to transmit data than XML in my opinion because it is very thin. This is done by creating the a ContactInfo object, a simple class I created to at as an entity for the data in this example. You can see the source in the attached zip file.
When the submit button is selected by the user the StoreContact function is called. This function gets the values in each of the inputs and assigns it to the corresponding property in the ContactInfo class. ASP.NET AJAX automatically lets you assign to the properties, so do not worry with this comes from. the ContactInfo entity is then passed to the PostContactRequest web method, along with two method callbacks, one for success and one for an exception. If the request is successful a confirmation message is displayed, if an exception occurs the user is alerted. This is not the most eloquent of solutions, but for demonstration purposes it will do.
function StoreContact() {
HideElement('dMakeContact');
HideElement('ContactResponse');
ShowElement('dThinking');
var FirstName = $get('FirstName');
var LastName = $get('LastName');
var Addr1 = $get('Addr1');
var Addr2 = $get('Addr2');
var City = $get('City');
var State = $get('txtState');
var Zip = $get('txtZip');
var Phone = $get('txtPhone');
var EMail = $get('EMail');
var Comment = $get('Comment');
var objCat = new ContactInfo();
objCat.FirstName = FirstName.value;
objCat.LastName = LastName.value;
objCat.Address1 = Addr1.value;
objCat.Address2 = Addr2.value
objCat.City = City.value;
objCat.State = State.value;
objCat.Zip = Zip.value;
objCat.phone = Phone.value;
objCat.Email = EMail.value;
objCat.Request = Comment.value;
ContactService.PostContactRequest(objCat, StoreCommentCompleteEvent, StoreCommentErrorEvent);
return false;
}
function StoreCommentCompleteEvent(result, context) {
HideElement('dThinking');
ShowElement('ContactResponse');
ContactResponse.innerHTML = ContactResponse.innerHTML + '<BR/>' + result;
}
function StoreCommentErrorEvent(result, context) {
alert('It blew up!');
if (null != result) {
alert(result.get_stackTrace());
}
}
This time the resulting request and response account for a total of 936 bytes, about a third of the previous example. While not a lot of difference in the short term it can and does add up over time. Plus you have all the overhead associated with rendering the UpdatePanel’s markup I did not go over. The less you have to move across the wire the faster the content should be rendered to the user and the fastest is the best on the web.
Reviewing the actual data transported over the wire reveals two separate JSON data packages. The response alone is around a fifth the size of the UpdatePanel’s response. I also think if you are trying to troubleshoot what is happening this is much easier to read, etc.
While the UpdatePanel version is faster to put in production, the more pure AJAX version is the more optimal performing version. If you are in the process of upgrading existing ASP.NET web forms code to leverage AJAX, then by all means slap the UpdatePanel on the page, but do make a real plan to wean yourself from the use of the UpdatePanel. If it is a brand new form I hope you consider using the AJAX with web service version, after all AJAX is all about leveraging web services with JavaScript. It takes a little more time, but after you do it a couple of times it becomes a pretty easy and natural task to tackle.
Download the Code