Creating Wizard in ASP.NET MVC (Part 3 - jQuery)
In Part 1 and
Part 2 of this article
series you developed a wizard in an ASP.NET MVC application using full page
postback and Ajax helper respectively. In this final part of this series you
will develop a client side wizard using jQuery. The navigation between various
wizard steps (Next, Previous) happens without any postback (neither full nor
partial). The only step that causes form submission to the server is clicking on
the Finish wizard button.
The jQuery version of the wizard uses only one view - Index - that contains
all the three steps as three <div> elements. Using jQuery code you show only one
<div> at a time giving a wizard appearance to the view.
The HomeController class now contains two overloads of Index() action method
as shown below:
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
[HttpPost]
public ActionResult Index(Customer obj)
{
if (ModelState.IsValid)
{
NorthwindEntities db = new NorthwindEntities();
db.Customers.Add(obj);
db.SaveChanges();
return View("Success");
}
return View();
}
}
As you can see the second Index() method is marked with [HttpPost] attribute
and accepts Customer parameter. Inside it checks whether there are any model
state errors or not. In case of any errors Index view is returned otherwise data
is added to the database and the Success view is returned.
Currently, there are no model validations on Customer class. To add
then using data annotations you can create a metadata class as shown below:
public class CustomerMetadata
{
[Required]
public string CustomerID { get; set; }
[Required]
public string CompanyName { get; set; }
[Required]
public string Address { get; set; }
[Required]
public string City { get; set; }
[Required]
public string Country { get; set; }
[Required]
public string PostalCode { get; set; }
[Required]
public string ContactName { get; set; }
[Required]
public string Phone { get; set; }
[Required]
public string Fax { get; set; }
}
[MetadataType(typeof(CustomerMetadata))]
public partial class Customer
{
}
As you can see the CustomerMetadata class defines all the properties that are
accepted through the wizard. All the properties are marked with [Required]
attribute. The CustomerMetadata class is linked with the Customer model class by
creating a partial class and then decorating it with [MetadataType] attribute.
Now, add the Index view to the project. The Index view that contains all
these <div> elements looks like this:
@model WizardInMVC.Models.Customer
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Index</title>
</head>
<body>
@using (Html.BeginForm("Index", "Home", FormMethod.Post))
{
<div id="divBasic">
<h1>Step 1 : Basic Details</h1>
@Html.LabelFor(m=>m.CustomerID)<br />
@Html.TextBoxFor(m=>m.CustomerID)
@Html.ValidationMessageFor(m=>m.CustomerID)<br />
@Html.LabelFor(m=>m.CompanyName)<br />
@Html.TextBoxFor(m=>m.CompanyName)
@Html.ValidationMessageFor(m=>m.CompanyName)
<br />
<input type="button" name="nextBtn" value='Next' />
</div>
<div id="divAddress">
<h1>Step 2 : Address Details</h1>
@Html.LabelFor(m=>m.Address)<br />
@Html.TextBoxFor(m=>m.Address)
@Html.ValidationMessageFor(m=>m.Address)
<br />
@Html.LabelFor(m=>m.City)<br />
@Html.TextBoxFor(m=>m.City)
@Html.ValidationMessageFor(m=>m.City)
<br />
@Html.LabelFor(m=>m.Country)<br />
@Html.TextBoxFor(m=>m.Country)
@Html.ValidationMessageFor(m=>m.Country)
<br />
@Html.LabelFor(m=>m.PostalCode)<br />
@Html.TextBoxFor(m=>m.PostalCode)
@Html.ValidationMessageFor(m=>m.PostalCode)
<br />
<input type="button" name="prevBtn" value='Previous' />
<input type="button" name="nextBtn" value='Next' />
</div>
<div id="divContact">
<h1>Step 3 : Contact Details</h1>
@Html.LabelFor(m=>m.ContactName)<br />
@Html.TextBoxFor(m=>m.ContactName)
@Html.ValidationMessageFor(m=>m.ContactName)
<br />
@Html.LabelFor(m=>m.Phone)<br />
@Html.TextBoxFor(m=>m.Phone)
@Html.ValidationMessageFor(m=>m.Phone)
<br />
@Html.LabelFor(m=>m.Fax)<br />
@Html.TextBoxFor(m=>m.Fax)
@Html.ValidationMessageFor(m=>m.Fax)
<br />
<input type="button" name="prevBtn" value='Previous' />
<input type="submit" name="nextBtn" value='Finish' />
</div>
}
</body>
</html>
As you can see there are three <div> elements namely divBasic, divAddress and
divContact. They contain various form fields for the respective wizard step. The
markup shown above is essentially the summation of the markups in individual
views from the Part 1
and Part 2 examples.
This markup is quite straightforward. There is a minor change though - the
Previous and Next buttons are of type button rather than submit. This is because
these buttons no longer cause form submission. To turn the above markup into a
wizard you need to write some jQuery code as shown below:
$(document).ready(function () {
$("div").hide();
$("div:first").show();
$(":button").click(function () {
var parentDiv = $(this).parent();
$("div").hide();
if($(this).val()=="Previous")
{
var prevDiv = parentDiv.prev();
prevDiv.show();
}
if ($(this).val() == "Next") {
var nextDiv = parentDiv.next();
nextDiv.show();
}
});
});
As you can see the above jQuery code first hides all the <div> elements from
the view. This is done using the element selector and hide() method. Then the
first <div> element is made visible using :first selector and show() method.
This way initially when the page loads only the first wizard step will be
visible.
The code then wires event handlers for the click event of the Previous and
Next buttons. This is done by selecting these buttons using :button selector and
then using click() method. Notice that inside the click event handler nowhere we
hardcode the IDs of various <div> elements. This way adding or removing a wizard
step doesn't need any change in the jQuery code. Inside the click event handler,
the code retrieves a reference to the parent <div> element of the button being
clicked. This is done using parent() method called on this. The code
then hides all the <div> elements. It then checks the button that was clicked -
Previous or Next. If Previous button is clicked a reference to the previous
<div> element is retrieved using prev() method called on parentDiv variable.
Similarly if Next button is clicked, a reference to the next <div> element is
retrieved using next() method called on parentDiv. Accordingly, either previous
<div> or next <div> is displayed using show() method.
That's it! Test the wizard by running the application and then navigating
between wizard steps.