Creating Wizard in ASP.NET MVC (Part 2)
In Part 1 of this
article series you developed a wizard in an ASP.NET MVC application. Although
the wizard developed in Part 1 works as expected it has one shortcoming. It
causes full page postback whenever you click on Previous or Next button. This
behavior may not pose much problem if a wizard has only a few steps. However, if
a wizard has many steps and each step accepts many entries then full page
postback can deteriorate the user experience. To overcome this shortcoming you
can add Ajax to the wizard so that only the form is posted to the server. In
this part of the series you will convert the application developed in
Part 1 to use Ajax. In
the next part you will further enhance the wizard using jQuery so that data is
posted to the server only on the final step.
To convert the wizard application developed previously to use Ajax you will
use Ajax helper of ASP.NET MVC. To use Ajax helper you need to make certain
changes to the application. These changes are listed below:
- All the four views namely BasicDetails, AddressDetails, ContactDetails
and Success will now be partial views.
- All the action methods will now return the corresponding partial views.
- The wizard will be launched by Index view and initially BasicDetails
partial view will be displayed.
You might wonder as to why we are making these changes. These changes are
required since we wish to use Ajax helper. The Ajax helper allows you to submit
a form using an Ajax request and the returned response is displayed in a DOM
element. For example, you may create a form that makes a post request to an
action method and displays a success message returned by the action method in a
<div> element. In our specific case when one wizard step is submitted to the
server using an Ajax request the server responds by sending the next or previous
wizard step. Thus a form submits to the server and renders another form in the
browser. This requires that the wizard steps return only the HTML
needed to display that step and not the page level items such as <script> and
<link>. The page level items just mentioned will go inside Index view that
launches the wizard.
Ok. Let's begin our development. Add an Index view to the project and key-in
the following markup into it:
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<script src="~/Scripts/jquery-1.10.2.js"></script>
<script src="~/Scripts/jquery.unobtrusive-ajax.js">
</script>
<title>Index</title>
</head>
<body>
<div id="divContainer">
@Html.Partial("BasicDetails")
</div>
</body>
</html>
Notice the above markup carefully. The <head> section of the page contains
<script> references to jquery-1.10.1.js and jquery.unobtrusive-ajax.js. These
files are necessary for the proper working of Ajax helper. Also notice that the
body section contains a <div> with ID of divContainer. This <div> plays an
important role in the functioning of the wizard because it houses various wizard
steps. As you can see from the code initially it hosts BasicDetails partial
view.
Now add a Partial View and name it as BasicDetails. The
BasicDetails partial view contains the following markup:
@model WizardInMVC.Models.BasicDetails
@{
AjaxOptions options = new AjaxOptions();
options.HttpMethod = "POST";
options.InsertionMode = InsertionMode.Replace;
options.UpdateTargetId = "divContainer";
}
@using (Ajax.BeginForm("BasicDetails","Home",options))
{
<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="submit" name="nextBtn" value='Next' />
}
The BasicDetails partial view has BasicDetails class as its data model. It
creates an instance of AjaxOptions class. The AjaxOptions class is used to
supply various configuration settings while using the Ajax helper. In this case
we set HttpMethod, InsertionMode and UpdateTargetId properties. The HttpMethod
property indicates the form submission method and it is set to POST in this
case. The InsertionMode and UpdateTargetId properties are very important for us.
The UpdateTargetId property indicates an ID of a DOM element that will be
updated with the response returned from the server. In our example
UpdateTargetId is divContainer, the <div> element you added inside the Index
view. The InsertionMode property governs how the server response should be added
to the UpdateTargetId. The InsertionMode has three possible values - InsertAfter,
InsertBefore and Replace. In our example we wish to replace the whole content of
divContainer with the response (i.e. a form making a wizard step) and we set it
to Replece.
Then a <form> is rendered using Ajax.BeginForm() helper method. The BeginForm()
method accepts three parameters viz. action method name, controller name and
AjaxOptions object. Inside it contains the same markup as the earlier example
(see Part 1 for more details).
On the same lines you need to add AddressDetails, ContactDetails
and Success partial views. The complete markup of these partial views is given below:
AddressDetails
@model WizardInMVC.Models.AddressDetails
@{
AjaxOptions options = new AjaxOptions();
options.HttpMethod = "POST";
options.InsertionMode = InsertionMode.Replace;
options.UpdateTargetId = "divContainer";
}
@using (Ajax.BeginForm("AddressDetails","Home",options))
{
<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="submit" name="prevBtn" value='Previous' />
<input type="submit" name="nextBtn" value='Next' />
}
ContactDetails
@model WizardInMVC.Models.ContactDetails
@{
AjaxOptions options = new AjaxOptions();
options.HttpMethod = "POST";
options.InsertionMode = InsertionMode.Replace;
options.UpdateTargetId = "divContainer";
}
@using (Ajax.BeginForm("ContactDetails","Home",options))
{
<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="submit" name="prevBtn" value='Previous' />
<input type="submit" name="nextBtn" value='Finish' />
}
Success
<h3>Customer Data Saved Successfully!</h3>
@Html.ActionLink("Add Another Customer","Index","Home")
Notice that all the partial views that use Ajax helper declare their own
AjaxOptions object and pass it to BeginForm() method.
Once you create all the partial views modify the action methods to return
partial view instead of view. The following code shows the modified BasicDetails()
action method:
[HttpPost]
public ActionResult BasicDetails(BasicDetails data,
string prevBtn, string nextBtn)
{
if (nextBtn != null)
{
if (ModelState.IsValid)
{
Customer obj = GetCustomer();
obj.CustomerID = data.CustomerID;
obj.CompanyName = data.CompanyName;
return PartialView("AddressDetails");
}
}
return PartialView();
}
As you can see there is no change in the logic of the action method. The only
difference is that instead of View() method it uses PartialView() method. Also
change AddressDetails() and ContactDetails() action method to return partial
views instead of views. If you run the wizard you will find that even after
navigating to different wizard steps the URL in the browser's address bar
remains unchanged indicating that Ajax requests are being made to the server
rather than full page postback.

As you can see even if you are on Step 2 the address bar still points to
/home/index.
That's it! In the next part you will learn to develop a wizard that replies
more on client side technologies and posts data to the server only at the last
step.