ScioSoft's Community Blogs Optimized IT musings for the technically inclined

Build an AJAX Contact Us Form using network callbacks in ASP.NET

by James Fielding 18. March 2010 13:53

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.

Browser view of the ContactUs web service

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.

Browser view of the Contact Us layout

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.

Browser view of the AJAX Contact Us page with a asynchronous network callback in 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.

Browser view of successful network callback

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

kick it on DotNetKicks.com
Shout it

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.

Share |

Tags: , , , , , ,

AJAX | Web Development | ASP.NET

Comments (11) -

Austin Buzhardt
Austin Buzhardt Germany
4/2/2010 10:56:39 AM #

Very informative post. I've found your site via Bing and I'm really glad about the information you provide in your articles. Btw your blogs layout is really broken on the Kmelon browser. Would be great if you could fix that. Anyhow keep up the good work!

ScioJim
ScioJim Canada
4/6/2010 2:47:51 PM #

We don't test against K-Meleon, but we do test against IE, Firfox, and Chrome. As such, I'd be inclined to think that it was a K-Meleon rendering issue. Sorry for the inconvenience.

James

asp script
asp script United States
4/3/2010 2:23:39 PM #

I like the blog, but could not find how to subscribe to receive the updates by email.

ScioJim
ScioJim
4/6/2010 2:49:50 PM #

At this time, we don't have an email newsletter, although this is something that we're looking at for the future. Thanks for the input.

James

best fake tan
best fake tan United Kingdom
4/8/2010 9:41:57 AM #

Thanks this is just what I was looking for, never played much with Ajax before so this was all a bit new to me but you detailed it perfectly, thanks again!

Sarah

PhpCatalog
PhpCatalog United States
4/16/2010 1:41:44 AM #

interesting blog… you could update it more frequently ..

ScioJim
ScioJim Canada
5/20/2010 8:55:37 PM #

This is true. Unfortunately, as of late it's been business before pleasure.

Thanks,
James

web directory free
web directory free United States
5/20/2010 2:31:43 PM #

I recently commented in your web log and picked notify me about new commentary. Is there a way to eliminate that service? I am receiving a considerable amount of mails.

ScioJim
ScioJim
5/20/2010 9:08:17 PM #

Use the contact us form at http://www.sciosoft.com/blogs/contact.aspx and provide the email address that is receiving the email notifications, and we'll see what we can do.

Sorry,
James

Mr. D
Mr. D Canada
9/15/2010 3:40:57 PM #

Hi, I cannot download your project, I get IIS7 error. Could you please update the link. Also is it possible to get the code in c#, It would help a lot. Thank you.

ScioJim
ScioJim Canada
9/15/2010 9:32:46 PM #

Sorry about that Mr.D. I think everything is now as it should be.

Regarding a C# version, this was orignally for a VB project we were doing, so I don't have C# source on hand, but you should be able to quickly convert it using one of the many free online vb-to-c# converters.  My personal favorite is: www.developerfusion.com/.../

I hope this helps,
James

Pingbacks and trackbacks (3)+

Comments are closed

RecentComments

Comment RSS

The opinions expressed herein are the personal opinions of the contributors and do not necessarily represent the views of Sciosoft Systems Inc.