ASP.NET MVC–Cascading Dropdown Lists Tutorial–Part 3: Cascading using Microsoft AJAX (Ajax.BeginForm helper)
- Part 1 – Defining the problem and the context
- Part 2 – Cascading using normal FORM post (Html.BeginForm helper)
- Part 3 – Cascading using Microsoft AJAX (Ajax.BeginForm helper)
- Part 4 – Cascading using FORM Hijaxing
- Part 5 – Cascading using jQuery.ajax():
- Part 6 – Creating a jQuery Cascade Select Plugin
Part 3 – Cascading using Microsoft AJAX (Ajax.BeginForm helper)
This method uses the Ajax.BeginForm helper. We will start by copying the DropDownNormalPostController from Part 1 and renaming it to DropDownAjaxPostController. Everything up to and including the Index action is the same as in the DropDownNormalPostController
public partial class DropDownAjaxPostController : Controller { private readonly IContinentRepository _continentRepository; // If you are using Dependency Injection, you can delete the following constructor public DropDownAjaxPostController( ) : this( new ContinentRepository( ) ) { } public DropDownAjaxPostController( IContinentRepository continentRepository ) { this._continentRepository = continentRepository; } public virtual ActionResult Index( ) { Atlas atlas = new Atlas( ); atlas.Continents = this._continentRepository.All; return View( atlas ); } }
@model Mvc3.Extensions.Demo.Areas.CascadingDropDownLists.Models.Atlas @{ ViewBag.Title = "Index"; } @Html.Partial( MVC.CascadingDropDownLists.DropDownAjaxPost.Views._Continents ) <div id="countries"> @Html.Partial( MVC.CascadingDropDownLists.DropDownAjaxPost.Views._Countries ) </div> <div id="cities"> @Html.Partial( MVC.CascadingDropDownLists.DropDownAjaxPost.Views._Cities ) </div> @DateTime.Now.ToString("dd/MM/yyyy HH:mm:ss:fff")
Notice the two div tags called “countries” and “cities”. We will use them to refresh only the Countries and the Cities depending on selection. Below are the partial views:
_Continents.cshtml:
@model Mvc3.Extensions.Demo.Areas.CascadingDropDownLists.Models.Atlas @using ( Ajax.BeginForm( MVC.CascadingDropDownLists.DropDownAjaxPost.SelectContinent(), new AjaxOptions { UpdateTargetId = "countries" } ) ) { <fieldset> <legend>Continents</legend> @Html.DropDownListFor( m => m.SelectedContinentId , new SelectList( Model.Continents , "Id" , "Name" ) , "[Please select a continent]" ) <input type="submit" value="Select" /> </fieldset> }
Now we are using Ajax.BeginForm instead of Html.BeginForm. The form posts to the same action and it will update the content of the “countries” div with the result (specified in the UpdateTargetId property).
The SelectContinent action:
[HttpPost] public virtual ActionResult SelectContinent( int? selectedContinentId ) { var countries = selectedContinentId.HasValue ? this._continentRepository.Find( selectedContinentId.Value ).Countries : null; Atlas atlas = new Atlas { SelectedContinentId = selectedContinentId , Continents = this._continentRepository.All , Countries = countries }; if ( Request.IsAjaxRequest( ) ) { return PartialView( MVC.CascadingDropDownLists.DropDownAjaxPost.Views._Countries , atlas ); } else { return View( MVC.CascadingDropDownLists.DropDownAjaxPost.Views.Index , atlas ); } }
The only difference here is the if block. If we have JavaScript enabled there will be an Ajax request and we return the _Countries partial view. When JavaScript is disabled it will behave exactly in Part1 (try this)
_Countries.cshtml:
@model Mvc3.Extensions.Demo.Areas.CascadingDropDownLists.Models.Atlas @if ( Model.Countries != null && Model.Countries.Count( ) > 0 ) { using ( Ajax.BeginForm( MVC.CascadingDropDownLists.DropDownAjaxPost.SelectCountry( ) , new AjaxOptions { UpdateTargetId = "cities" } ) ) { <fieldset> <legend>Countries</legend> @Html.HiddenFor( m => m.SelectedContinentId ) @Html.DropDownListFor( m => m.SelectedCountryId , new SelectList( Model.Countries , "Id" , "Name" ) , "[Please select a country]" ) <input type="submit" value="Select" /> </fieldset> } } else { <fieldset> <legend>Countries</legend> No information available </fieldset> }
and the SelectCountry action:
[HttpPost] public virtual ActionResult SelectCountry( int? selectedContinentId , int? selectedCountryId ) { var selectedContinent = selectedContinentId.HasValue ? this._continentRepository.Find( selectedContinentId.Value ) : null; var countries = ( selectedContinent != null ) ? selectedContinent.Countries : null; var cities = ( countries != null && selectedCountryId.HasValue ) ? countries.Where( c => c.Id == selectedCountryId.Value ).SingleOrDefault( ).Cities : null; Atlas atlas = new Atlas { SelectedContinentId = selectedContinentId , SelectedCountryId = selectedCountryId , Continents = this._continentRepository.All , Countries = countries , Cities = cities }; if ( Request.IsAjaxRequest( ) ) { return PartialView( MVC.CascadingDropDownLists.DropDownAjaxPost.Views._Cities , atlas ); } else { return View( MVC.CascadingDropDownLists.DropDownAjaxPost.Views.Index , atlas ); } }
Again, the only difference is the if block.
_Cities.cshtml:
The _Cities.cshtml partial view is the same as in Part 1.
See it in action
Cascading Dropdown Lists - Ajax FORM post
Notice that there is no change in the datetime value at the bottom of the Index view, meaning that we do a partial page refresh. The only problem that remains is the need to click the Select buttons.