ASP.NET MVC–Cascading Dropdown Lists Tutorial–Part 3: Cascading using Microsoft AJAX (Ajax.BeginForm helper)


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 );
    }
}

Index.cshtml:

@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.

Download

 

4 Comments

  • Hi all,

    I dont understand why im getting this error:

    Error 21 Cannot implicitly convert type 'System.Linq.IQueryable' to 'System.Collections.Generic.IEnumerable'. An explicit conversion exists (are you missing a cast?)

    i get this in VS on line:

    atlas.Continents = this._continentRepository.All;

    pls help.

  • @hrvojeBarbaric: try using atlas.Continents = this._continentRepository.All.ToList();

  • thx for replay. same problem. Im new in mvc3 so i wanna know why i get this error. in orginal code that I download from this site it all ok. but when i wanna use this code for some other website i get this error. only diffrence is that i use database and in this example is dummy data. am i doing somethink wrong with entites and relations so VS tell me that he cant convert IQueryable to IEnumerable?

  • I solve it. I was connecting atlas.Continents with wrong repository. so stupid mistake. thx for your help :)

Comments have been disabled for this content.