As you can see when a user visits our site with JavaScript enabled and clicks on the Contact Us link, they are presented with a nice jQuery UI dialog window. They can fill in the form and get a nice confirmation message inside the dialog, and finally close it without ever leaving the page they were on.
A visitor without JavaScript will still get the same functionality, just a slightly lesser experience. Without JavaScript logic attached to our Contact Us link it behaves like a plain old hyperlink, navigating the browser to a new page. Once they fill out the form and press Send Message we redirect them back to the Home page with a confirmation message.
You may notice that very little server-side code is required to achieve the functionality seen in the screenshots above.
[HttpGet]
public ActionResult ContactUs()
{
if (Request.IsAjaxRequest())
{
return PartialView("_ContactUs");
}
return View();
}
[HttpPost]
public ActionResult ContactUs(ContactUsInput input)
{
// Validate the model being submitted
if (!ModelState.IsValid)
{
// If the incoming request is an Ajax Request
// then we just return a partial view (snippet) of HTML
// instead of the full page
if (Request.IsAjaxRequest())
return PartialView("_ContactUs", input);
return View(input);
}
// TODO: A real app would send some sort of email here
if (Request.IsAjaxRequest())
{
// Same idea as above
return PartialView("_ThanksForFeedback", input);
}
// A standard (non-Ajax) HTTP Post came in
// set TempData and redirect the user back to the Home page
TempData["Message"] = string.Format("Thanks for the feedback, {0}! We will contact you shortly.", input.Name);
return RedirectToAction("Index");
}
Â
One nice thing about a new MVC 3 site is that the VS Project Template actually comes with all the scripts necessary to make this work. Just make sure you reference the following scripts in your _Layout.cshtml (or Master Page if you donât yet speak Razor)
* Note: for brevity I am putting the jQuery logic at the bottom of my Layout but in a real-world application this should be moved to an external .js file.
The jQuery code below operates on the following conventions:
<script src="@Url.Content("~/Scripts/jquery-1.4.4.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery-ui.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.unobtrusive-ajax.js")" type="text/javascript"></script>
<script type="text/javascript">
$.ajaxSetup({ cache: false });
$(document).ready(function () {
$(".openDialog").live("click", function (e) {
e.preventDefault();
$("<div></div>")
.addClass("dialog")
.attr("id", $(this).attr("data-dialog-id"))
.appendTo("body")
.dialog({
title: $(this).attr("data-dialog-title"),
close: function () { $(this).remove() },
modal: true
})
.load(this.href);
});
$(".close").live("click", function (e) {
e.preventDefault();
$(this).closest(".dialog").dialog("close");
});
});
</script>
Â
The Contact Us hyperlink is just a standard ActionLink with some additional attributes added.
* Note: Sweet! The MVC 3 helpers convert the underscore in HTML attributes to a dash when rendered!
@Html.ActionLink("Contact Us", "ContactUs", "Home", null,
new { @class = "openDialog", data_dialog_id = "emailDialog", data_dialog_title = "Contact Us"})
Â
The ActionLink renders the following HTML
<a class="openDialog" data-dialog-id="emailDialog"
data-dialog-title="Contact Us" href="/Home/ContactUs">Contact Us</a>
We need to create the following views to wrap up this feature for both the Ajax and plain view.
The partial view (following the Razor/MVC/RoR convention of prefixing partial views with an underscore) is used in both the Ajax experience and the standard experience. This way we donât need to duplicate the same form in 2 places.
I am using Ajax.BeginForm since MVC 3 renders unobtrusive HTML-5-compatible Ajax attributes, which will will behave perfectly in down-level browsers. Using Ajax.BeginForm gives us client-side and server-side validation in both the Ajax-dialog view and the standard view.
*Note: The UpdateTargetId must match the ID of the dialog window we open with jQuery (in this example I am passing the ID in the Html.ActionLink using data_dialog_id). This makes sure that when the form submits and returns a response the HTML will be properly injected into our dialog window.
@model ProgressiveEnhancement.Models.ContactUsInput
@using (Ajax.BeginForm(new AjaxOptions { HttpMethod = "POST", UpdateTargetId = "emailDialog" }))
{
@Html.ValidationSummary(true)
@Html.EditorForModel()
<p>
<input type="submit" value="Send Message!" />
</p>
}
Â
Below is the full ContactUs view, used for non-Ajax requests. In this example it just adds a nice <h2> to the page (that we donât want in our Ajax Dialog view) and then delegates the rest of the rendering to the partial view created above.
@model ProgressiveEnhancement.Models.ContactUsInput
<h2>Contact Us</h2>
@Html.Partial("_ContactUs")
Â
@model ProgressiveEnhancement.Models.ContactUsInput
<p>Thanks for your feedback, @Model.Name! We will contact you shortly.</p>
<p><a href="javascript:void(0);" class="close">Close Window</a></p>
Â
Hopefully this tutorial covered everything! Itâs exciting to see how far MVC 3 and jQuery have come to really make progressive enhancement a reality with very minimal effort.
Â
<â For reference the final solution structure ended up looking like this
Â
VS 2010 with ASP.NET MVC 3 required to open it
Â
Leave a Comment