January 2018 : Instructor-led Online Course in ASP.NET Core 2.0. Conducted by Bipin Joshi. Read more...
Registration for January 2018 batch of ASP.NET Core 2.0 instructor-led online course has already started. Conducted by Bipin Joshi. Register today ! Click here for more details.

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.

 


Bipin Joshi is a software consultant, an author and a yoga mentor having 22+ years of experience in software development. He also conducts online courses in ASP.NET MVC / Core and Design Patterns. He is a published author and has authored or co-authored books for Apress and Wrox press. Having embraced the Yoga way of life he also teaches Ajapa Yoga to interested individuals. To know more about him click here.

Get connected : Twitter  Facebook  Google+  LinkedIn

Posted On : 12 January 2015


Tags : ASP.NET MVC jQuery JavaScript