ASP.NET MVC–Cascading Dropdown Lists Tutorial–Part 5.1: Cascading using jQuery.ajax() ($.ajax() and DOM Objects)


Part 5.1: Cascading using jQuery.ajax() ($.ajax() and DOM Objects)

In this part we will use ASP.NET MVC to create an application that will act more like a JSON service than a Web application. For start let’s create a new controller called DropDownjQueryAjaxPostController:

public partial class DropDownjQueryAjaxPostController : Controller
{
    private readonly IContinentRepository _continentRepository;
    private readonly ICountryRepository _countryRepository;
    private readonly ICityRepository _cityRepository;
    // If you are using Dependency Injection, you can delete the following constructor
    public DropDownjQueryAjaxPostController( ) 
        : this( new ContinentRepository( ) , new CountryRepository( ) , new CityRepository( ) ) { }
    public DropDownjQueryAjaxPostController( 
        IContinentRepository continentRepository , 
        ICountryRepository countryRepository , 
        ICityRepository cityRepository )
    {
        this._continentRepository = continentRepository;
        this._countryRepository = countryRepository;
        this._cityRepository = cityRepository;
    }
    public virtual ViewResult Index( )
    {
        return View( );
    }
}

Nothing unusual here. We set up repositories and the Index action. The unusual thing is that we return the Index view without any model and that the Index view is the only view we have:

@{
    ViewBag.Title = "Index";
}
<fieldset>
    <legend>Continents</legend>
    <select id='continents'>
        <option value=''>[Please select a continent]</option>
    </select>
</fieldset>
<fieldset>
    <legend>Countries</legend>
    <div id="countriesContainer">
        <select id='countries' style='display: none;'>
            <option value=''>[Please select a country]</option>
        </select>
        <span id='noInfoCountries'>No information available </span>
    </div>
</fieldset>
<fieldset>
    <legend>Cities</legend>
    <div id="citiesContainer">
        <table id='cities' style='display: none;'>
            <tr>
                <th>
                    Name
                </th>
                <th>
                    Population
                </th>
            </tr>
        </table>
        <span id='noInfoCities'>No information available </span>
    </div>
</fieldset>

Let’s see what we have:

  • An empty continents dropdown list (we could fill the continents from the view but we’ll see another way)
  • An empty hidden countries dropdown list  and a “No information available” span that will be visible if there is no continent selected
  • An empty hidden cities table and another “No information available” span that will be visible if there is no country selected

So the first thing that we have to do is to fill the continents dropdown list when the page loads:

<script type='text/javascript'>
    $(document).ready(function () {
        //Load the continents
        $.ajax({
            url: '@Url.Action( MVC.CascadingDropDownLists.DropDownjQueryAjaxPost.GetContinents( ) )',
            type: 'GET',
            success: function (data) {
                var contients = $('#continents');
                var domContinents = contients.get(0); // $('#id') != document.getElementById('id')
                //Empty the continents dropdown list
                for (var i = domContinents.options.length - 1; i > 0; i--) {
                    domContinents.remove(i);
                }
                for (var i = 0; i < data.length; i++) {
                    var item = data[i];
                    var continentOption = new Option(item.Name, item.Id);
                    contients.append(continentOption);
                }
            }
        });
    });
</script>

We use the jQuery.ajax () method (or $.ajax() for short) to perform a Ajax Get request on the GetContinents action. If the call succeds we clear the contents of the dropdownlist and fill it with the data received. Nothing to complicated, just javascript objects and method calls. The only odd thing is the continents and domContinents (see the comment in the code above).

Next we need to catch the change event for the continents dropdown list and perform another Ajax Get request to get the countries for the selected continent:

//Catch the continents change event
$('#continents').live('change', function () {
    var countries = $('#countries');
    var noInfo = $('#noInfoCountries');
    var domCountries = countries.get(0);
    for (var i = domCountries.options.length - 1; i > 0; i--) {
        domCountries.remove(i);
    }
    $('#cities').hide();
    $('#noInfoCities').show();
    if ($('#continents option:selected').val() != '') {
        $.ajax({
            url: '@Url.Action( MVC.CascadingDropDownLists.DropDownjQueryAjaxPost.GetCountries( ) )',
            data: { continentId: $('#continents option:selected').val() },
            type: 'GET',
            success: function (data) {
                if (data.length > 0) {
                    for (var i = 0; i < data.length; i++) {
                        var item = data[i];
                        var countryOption = new Option(item.Name, item.Id);
                        countries.append(countryOption);
                    }
                    noInfo.hide();
                    countries.show();
                }
                else {
                    countries.hide();
                    noInfo.show();
                }
            }
        });
    }
    else {
        countries.hide();
        noInfo.show();
    }
});

When the continents dropdown list selection changes we empty the countries dropdown list. The request is only performed if there is a continent selected, otherwise the “No Information Available” span becomes visible. The span becomes visible also if there are no countries in the continent (for Antarctica).

Next we catch the countries dropdown list change event:

//Catch the countries change event
$('#countries').live('change', function () {
    var cities = $('#cities');
    var domCities = cities.get(0);
    for (var i = domCities.rows.length - 1; i > 0; i--) {
        domCities.deleteRow(i);
    }
    var noInfo = $('#noInfoCities');
    if ($('#countries option:selected').val() != '') {
        $.ajax({
            url: '@Url.Action( MVC.CascadingDropDownLists.DropDownjQueryAjaxPost.GetCities( ) )',
            data: { countryId: $('#countries option:selected').val() },
            type: 'GET',
            success: function (data) {
                if (data.length > 0) {
                    for (var i = 0; i < data.length; i++) {
                        var item = data[i];
                        var lastRow = domCities.rows.length;
                        var cityRow = domCities.insertRow(lastRow);
                        var cityName = cityRow.insertCell(0);
                        cityName.innerHTML = item.Name;
                        var cityPopulation = cityRow.insertCell(1);
                        cityPopulation.innerHTML = item.Population.toString();
                        cityPopulation.align = 'right';
                    }
                    noInfo.hide();
                    cities.show();
                }
                else {
                    cities.hide();
                    noInfo.show();
                }
            }
        });
    }
    else {
        countries.hide();
        noInfo.show();
    }
});

This is it. Not to complicated but there is some code to write.

See it in action

Cascading Dropdown Lists - jQuery.ajax()

There are some things that can be improved in this method:

  • Using the DOM objects is kind of ugly (adding and removing options and rows)
  • There are a lot of ifs to hide and show elements depending on selections

We will correct them in the next 2 parts

Download
 

7 Comments

  • @Omu: True but there are 2 sides of this tutorial. One is to show how to cascade dropdown lists using various methods and the other is to show some tehniques that can be used in a ASP.NET MVC application

  • Very helpful series... thanks Radu.

    I thought I'd point out for the benefit of your readers that the GetContinents() method is not generated by the T4MVC controller template---and is not listed in this blog post itself. From the downloaded code, it is:

    [HttpGet]
    public virtual ActionResult GetContinents()
    {
    var continents =
    (
    from continent in this.continentRepository.All
    select new
    {
    Id = continent.Id,
    Name = continent.Name
    }
    ).ToList();

    return Json(continents, JsonRequestBehavior.AllowGet);
    }

  • Hi Martin,

    Thanks for pointing that out.

  • thanks @Radu for your effort and this awesome series and @Omu for the nice control

  • Hi,

    This approach does not works in Internet Explorer 8, the options of dropdown are blank.

    You have any idea to resolve this ?

    I tried in Internet Explorer 9 in compatible mode for IE 8.

    Thanks.

  • I discovered one solution for problem with IE8, instead of
    >>var continentOption = new Option(item.Name, item.Id); <>var continentOption = '' + item.Name + '';.

    This will resolve a problem with IE8.

  • @Leandro, sorry for not answering sooner. I still have to discover how the spam rating works because lot of good comments end up as spam.

    Thank you for coming up with a solution for the problem. I must admit that my preferred way of doing cascading is by using Knockout.js instead of working directly with the DOM objects

Comments have been disabled for this content.