Intro | Part 1 | Part 2
So we're on the third instalment of this three-part tutorial series, where you and I are building an AJAX Contact Us form in ASP.NET, a couple of different ways. If you need a recap, here are the links to the introduction and the partial-page updates method. In this article we’re going to put the "less is more" principle into action by initiating our network callbacks directly from client-side JavaScript code, and we'll also build an AJAX-enabled web service to handle our call. As in Part 1, I’m going to assume that you are comfortable using Visual Studio.
You can download the source files for this project AJAXEnabledContactForm.zip (45.6 kb).
So we're going from partial-page updates using the asp:UpdatePanel, to network callbacks through a web service using HTML controls. The main reason you'd go this way is to drastically reduce your data transfer between the client and server. And why is this? Well, for two reasons: First, unlike partial-page updates, we're not passing all the form's data back to the server, just the required parameters in an XML wrapper. Second, we're not executing the page's full life-cycle back on the server from the postback, either. So if you're looking for efficiency, this is the way to go.
We'll be continuing on with the project that we created in Part 1, which we called “AjaxEnabledContactForm”. Let's start with the our web service, which is going to provide the service methods that our Contact Us form calls. So I`ve created a new web service called "ContactUs.asmx". Since we've already seen the some of this code in Part 1, I'll give you the full page first, and then we'll walk through it.
Imports System.Web.Services
Imports System.Web.Services.Protocols
Imports System.ComponentModel
<System.Web.Script.Services.ScriptService()> _
<System.Web.Services.WebService(Namespace:="http://xmlforasp.net")> _
<System.Web.Services.WebServiceBinding(ConformsTo:=WsiProfiles.BasicProfile1_1)> _
Public Class ContactUs
Inherits System.Web.Services.WebService
<WebMethod()> _
Public Function Submit(ByVal strEmail As String, ByVal strComment As String) As Boolean
System.Threading.Thread.Sleep(5000)
Return SendMail(strEmail, strComment)
End Function
Private Function SendMail(ByVal strEmail As String, ByVal strComment As String) As Boolean
Dim mailMessage As System.Net.Mail.MailMessage = New System.Net.Mail.MailMessage()
mailMessage.From = New System.Net.Mail.MailAddress("myserver@mysite.com")
mailMessage.To.Add(New System.Net.Mail.MailAddress("me@mysite.com"))
mailMessage.ReplyTo = New System.Net.Mail.MailAddress(strEmail.Trim())
'Set additional options
mailMessage.Priority = Net.Mail.MailPriority.Normal
mailMessage.IsBodyHtml = False
'Set the subjet and body text
mailMessage.Subject = "Contact Us Form from: " & strEmail.Trim
mailMessage.Body = strComment
'Create an instance of the SmtpClient class for sending the email
'using web.config settings
Dim smtpClient As System.Net.Mail.SmtpClient = New System.Net.Mail.SmtpClient()
Try
smtpClient.Send(mailMessage)
Catch ex As System.Net.Mail.SmtpException
Return False
Catch ex As Exception
Return False
Finally
mailMessage.Dispose()
End Try
Return True
End Function
End Class
The nice thing about doing our Contact Us form through a web service is that we can have both as a separate applications in our production evironment, so if there is a problem in our main application, the service is still available for other applications, or vice-versa. Web services also lend themselves well to being configured to record submissions to a database. Note: Our setup could be significantly improved by making the service multi-tier, and dealing with the service's security, but what we've done today will definitely get you started.
So to step through this code, the first thing we did was make our service methods callable by JavaScript. That's why we added the following attribute:
<System.Web.Script.Services.ScriptService()> _
This magic little snippet allows the ASP.NET AJAX extension runtime to know that we're going to call our web method from JavaScript.
For our Submit web method, we've got:
System.Threading.Thread.Sleep(5000)
Return SendMail(strEmail, strComment)
So first we're faking network latency (so remember to take this out in production) by having the service sleep for 5 seconds, before proceeding to run our SendMail function. I'm not going to explain the SendMail function, because it is basically the same as what we saw in Part 1. The main difference is that instead of a subroutine, we've got a function that returns true if sending the email works out, and false if it doesn't.
At this point, you can test out the web service if you'd like, by firing up Visual Studio debugging, with ContactUs.asmx as the start page.

Great. So now we need a Contact Us page. We'll create a new .ASPX page and call it "NetworkCallback". The body tag for the page is as follows:
<body>
<form id="form1" runat="server">
<asp:ScriptManager ID="ScriptManager1" runat="server" />
<div>
<span>Email</span><br />
<input id="txtEmail" type="text" /><br />
<span>Comments</span><br />
<textarea id="txtComments" cols="20" rows="4"></textarea><br />
<input id="btnSubmit" type="submit" value="submit" title="Submit"
onclick="return btnSubmit_onclick()" />
</div>
</form>
</body>
So, the page looks exactly the same as before, except we've swapped out the ASP controls for HTML. We didn't have to do this, but since we've gone from partial-page updates to network callbacks, we might as well save the server processing on our controls, too.

Just for fun, and to give our users some AJAX feedback, let's throw some progress updates into our HTML, right below our submit button:
<div id="divWaiting" style="display: none">
<img alt="UpdateProgress" src="Images/SquareCircleLoader.gif"
style="width: 31px; height: 31px" />
</div>
<div id="divSuccess" style="display: none">
Thank you for your submission.
</div>
<div id="divFailure" style="display: none">
There was a problem with your request.
</div>
In the above code we've got three divs: The first one is displayed after the user has submitted the form, and is waiting for it to be processed by the server. The second is displayed after the form has been processed. The third shows up if there is a problem. By default, none of these divs are visible to the user, so we obviously we need to wire these divs up somehow. Here's the JavaScript that makes things happen:
<script language="javascript" type="text/javascript">
<!--
function btnSubmit_onclick() {
document.getElementById('divSuccess').style.display = "none";
document.getElementById('divFailure').style.display = "none";
document.getElementById('divWaiting').style.display = "block";
var txtEmail = document.getElementById('txtEmail').value;
var txtComments = document.getElementById('txtComments').value;
AJAXEnabledContactForm.ContactUs.Submit(txtEmail, txtComments, OnSuccess, OnFailure);
return false
}
function OnSuccess(arg) {
document.getElementById('divWaiting').style.display = "none";
if (arg) {
document.getElementById('divSuccess').style.display = "block";
}
else {
document.getElementById('divFailure').style.display = "block";
return false;
}
}
function OnFailure(error) {
document.getElementById('divFailure').style.display = "block";
document.getElementById('divWaiting').style.display = "none";
alert("Stack Trace: " + error.get_stackTrace() + "/r/n" +
"Error: " + error.get_message() + "/r/n" +
"Status Code: " + error.get_statusCode() + "/r/n" +
"Exception Type: " + error.get_exceptionType() + "/r/n" +
"Timed Out: " + error.get_timedOut());
}
// -->
</script>
So before my SEO friends jump all over me, I realize that I put the JavaScript in the <head> tag in the source file download. The JavaScript should really go just before the end </body> tag of NetworkCallback.aspx. My apologies...old habits really do die hard. One more thing, on error you'll get an alert box describing the error. This is a handy JavaScript snippet for troubleshooting, but remember to take it out before you drop this into production.
Now to bring this all together, we've got our one and only instance of the ScriptManager on the page, just after the form tag. In order for our ScriptManager to access our web service, the ScriptManager needs to know about the web service. And to do that, we just need to tell the ScriptManager where to find our web service, and it will take care of the actual implementation:
<asp:ScriptManager ID="ScriptManager1" runat="server">
<Services>
<asp:ServiceReference Path="~/ContactUs.asmx" />
</Services>
</asp:ScriptManager>
So let's just walk through this. When our user clicks the submit button, the contents of the email and comments text boxes are submitted to our web service asynchronously using XML, and our page tells the user to wait as something is happening. When the web service finally sends the email, the client is updated with the good news. If there is a problem, the user is also notified. We could also tie this into Part 1, by adding some JavaScript redirects to our Success.aspx and Failed.aspx pages, but I'll leave that for you to implement as you see fit.
Go ahead and give it a try running the NetworkCallback.aspx page. After you fill in the text boxes and click the submit button, you should get 5 seconds of progress.

This is followed by an email delivered to your hard drive at c:\Temp\ (as just like in Part 1, we're using the Specified Pickup Directory method to handle our email so you'll need to add that to the web.config if you're building your own project), and a message saying that everything went well.

Well, now you've got two ways of building that amazing AJAXified Contact Us page you've been dreaming about. As before, at a minimum, you MUST add some server-side validation/security before you contemplate dropping this code into production, otherwise you're leaving the door wide open to hackers. You may also want to look at securing your web service, or risk becoming a spammer's delight. It's not production code, but your definitely on the right path.
Happy network callbacking,
James Fielding
Sciosoft Systems is a Canadian web design & development company based in Muskoka,
which is in central Ontario. We provide ASP.NET website & Windows Server application
development services to small and medium-sized business, as well as local government
and not-for-profit groups. If you have a website project you’d like to discuss,
please visit us at www.sciosoft.com.