In this article I will explain with an example, how to populate Cascading i.e. dependent Country, State and City DropDownLists using jQuery AJAX in ASP.Net Core Razor Pages.
The Cascading i.e. dependent Country, State and City DropDownLists will be populated from database using jQuery AJAX in ASP.Net Core Razor Pages.
Note: For beginners in ASP.Net Core Razor Pages, please refer my article ASP.Net Core Razor Pages: Hello World Tutorial with Sample Program example.
 
 
Database
For this example I have used the following three tables Countries, States and Cities with the schema as follow.
Countries Table
ASP.Net Core Razor Pages: Cascading (Dependent) Country State City DropDownLists using jQuery AJAX
 
States Table
ASP.Net Core Razor Pages: Cascading (Dependent) Country State City DropDownLists using jQuery AJAX
 
Cities Table
ASP.Net Core Razor Pages: Cascading (Dependent) Country State City DropDownLists using jQuery AJAX
 
Note: You can download the database table SQL by clicking the download link below.
          Download SQL file
 
 
Configuring the Anti-Forgery Token and JSON Serializer setting
The first step is to configure the Anti-Forgery Token and JSON Serializer settings in the Startup.cs file.
1. Open the Startup.cs class from the Solution Explorer window.
ASP.Net Core Razor Pages: Cascading (Dependent) Country State City DropDownLists using jQuery AJAX
 
2. Add the following namespaces.
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json.Serialization;
 
3. Then inside the ConfigureServices method, you will have to add the following code which will instruct the program to:
1. Add MVC Services for Razor Pages.
2. Use Newtonsoft JSON for serialization.
3. Add Anti-Forgery Token with specific name to the Form.
public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc()
            .SetCompatibilityVersion(CompatibilityVersion.Version_2_1)
            .AddJsonOptions(options => options.SerializerSettings.ContractResolver = new DefaultContractResolver());
    services.AddAntiforgery(o => o.HeaderName = "XSRF-TOKEN");
}
 
 
 
Model
The following Model class consists of three Generic List Collection properties of SelectListItem class for holding the Country, State and City records.
The Model class also contains three Integer type properties for holding the selected Country, State and City values.
Below the class, there are three classes Country, State and City which will be used to connect to the database table using Entity Framework.
public class CascadingModel
{
    public CascadingModel()
    {
        this.Countries = new List<SelectListItem>();
        this.States = new List<SelectListItem>();
        this.Cities = new List<SelectListItem>();
    }
 
    public List<SelectListItem> Countries { get; set; }
    public List<SelectListItem> States { get; set; }
    public List<SelectListItem> Cities { get; set; }
 
    public int CountryId { get; set; }
    public int StateId { get; set; }
    public int CityId { get; set; }
}
 
 
Database Context
Once the Entity Framework is configured and connected to the database table, the Database Context will look as shown below.
Note: For beginners in ASP.Net Core Razor Pages and Entity Framework, please refer my ASP.Net Core Razor Pages: Simple Entity Framework Tutorial with example. It covers all the information needed for connecting and configuring Entity Framework with ASP.Net Core Razor Pages.
 
using Microsoft.EntityFrameworkCore;
 
namespace Cascading_DropDownList_Razor_Core
{
    public class DBCtx : DbContext
    {
        public DBCtx(DbContextOptions<DBCtx> options) : base (options)
        {
        }
 
        public DbSet<Country> Countries { get; set; }
        public DbSet<State> States { get; set; }
        public DbSet<City> Cities { get; set; }
    }
 
    public class Country
    {
        public int CountryId { get; set; }
        public string CountryName { get; set; }
    }
 
    public class State
    {
        public int StateId { get; set; }
        public string StateName { get; set; }
        public int CountryId { get; set; }
    }
 
    public class City
    {
        public int CityId { get; set; }
        public string CityName { get; set; }
        public int StateId { get; set; }
    }
}
 
 
Razor PageModel (Code-Behind)
The PageModel consists of following three Handler methods.
Handler method for handling GET operation
Inside this Handler method, the records from Countries table is fetched using Entity Framework and then a Generic List collection of SelectListItem class objects is generated.
The generated Generic List collection of SelectListItem is assigned to the Countries property of the Model class object which is then returned back to the Razor Page.
 
Handler method for handling AJAX operation
This Handler method handles call made by the jQuery AJAX function in the Razor Page.
Note: The following Handler method handles POST call and will return JSON object and hence the return type is set to JsonResult. For more details please refer Using jQuery AJAX in ASP.Net Core Razor Pages.
 
Attributes
ValidateAntiForgeryToken: The ValidateAntiForgeryToken attribute is used to prevent cross-site request forgery attacks.
Note: A cross-site request forgery is an attack is done by sending harmful script element, malicious command, or code from the user’s browser.
 
This Handler method accepts type and value parameters and based on the type i.e. Country or State, the Generic List collection of States or Cities respectively are fetched from the database using Entity Framework and returned to Razor page in JSON format.
 
Handlermethod for handling POST operation
This Handler method handles the call made from the POST function from the Razor Page and is executed when the Submit button is clicked.
When the Form is submitted, the posted values are captured in three variables one for each i.e. Country, State and City.
Using these values, the Countries, States and Cities are again populated using Entity Framework in the Model class object which is then returned back to the Razor Page.
public class IndexModel : PageModel
{
    private DBCtx Context { get; }
    public IndexModel(DBCtx _context)
    {
        this.Context = _context;
    }
 
    public CascadingModel CascadingModel { get; set; }
 
    public void OnGet()
    {
        CascadingModel = new CascadingModel();
        CascadingModel.Countries = (from customer in this.Context.Countries
                                   select new SelectListItem
                                   {
                                       Value = customer.CountryId.ToString(),
                                       Text = customer.CountryName
                                   }).ToList();
    }
 
    [ValidateAntiForgeryToken]
    public JsonResult OnPostAjaxMethod(string type, int value)
    {
        CascadingModel = new CascadingModel();
        switch (type)
        {
            case "ddlCountries":
                CascadingModel.States = (from customer in this.Context.States
                                         where customer.CountryId == value
                                         select new SelectListItem
                                         {
                                             Value = customer.StateId.ToString(),
                                             Text = customer.StateName
                                         }).ToList();
                break;
            case "ddlStates":
                CascadingModel.Cities = (from customer in this.Context.Cities
                                         where customer.StateId == value
                                         select new SelectListItem
                                         {
                                             Value = customer.CityId.ToString(),
                                             Text = customer.CityName
                                         }).ToList();
                break;
        }
        return new JsonResult(CascadingModel);
    }
 
    public void OnPostSubmit(int countryId, int stateId, int cityId)
    {
        CascadingModel = new CascadingModel();
        CascadingModel.Countries = (from customer in this.Context.Countries
                                    select new SelectListItem
                                    {
                                        Value = customer.CountryId.ToString(),
                                        Text = customer.CountryName
                                    }).ToList();
 
        CascadingModel.States = (from customer in this.Context.States
                                 where customer.CountryId == countryId
                                 select new SelectListItem
                                 {
                                     Value = customer.StateId.ToString(),
                                     Text = customer.StateName
                                 }).ToList();
 
        CascadingModel.Cities = (from customer in this.Context.Cities
                                 where customer.StateId == stateId
                                 select new SelectListItem
                                 {
                                     Value = customer.CityId.ToString(),
                                     Text = customer.CityName
                                 }).ToList();
 
        CascadingModel.CountryId = countryId;
        CascadingModel.StateId = stateId;
        CascadingModel.CityId = cityId;
    }
}
 
 
Razor Page (HTML)
The HTML of Razor Page consists of an HTML Form which has been created using following attribute.
method – It specifies the Form Method i.e. GET or POST. In this case it will be set to POST.
The Anti-Forgery Token has been added to Razor Page using the AntiForgeryToken function of the HTML Helper class.
Note: For more details please refer my article, Sending AntiForgeryToken with AJAX requests in ASP.Net Core Razor Pages.
 
The Form consists of three HTML DropDownList elements (SELECT) and a Submit Button.
The Submit Button has been set with the POST Handler method using the asp-page-handler attribute.
Note: In the Razor PageModel, the Handler method name is OnPostSubmit but here it will be specified as Submit when calling from the Razor HTML Page.
 
The Model properties have been assigned to the DropDownLists using the asp-items Tag Helpers attribute.
Each DropDownList has been assigned a jQuery OnChange event handler, when an item is selected in the DropDownList, an AJAX call is made to the AjaxMethod Handler method and based on the type value, the appropriate DropDownList is populated inside the Success event handler from the JSON result.
When the Submit Button is clicked, the Form gets submitted and the selected Country, State and City values are sent to the Controller.
Finally, inside the jQuery document ready event handler, if all the three DropDownLists are populated then the selected values are displayed using JavaScript Alert Message Box.
@page
@model Cascading_DropDownList_Razor_Core.Pages.IndexModel
@addTagHelper*, Microsoft.AspNetCore.Mvc.TagHelpers
 
@{
    Layout = null;
}
 
<!DOCTYPE html>
 
<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Index</title>
</head>
<body>
    @Html.AntiForgeryToken()
    <form method="post">
        <select id="ddlCountries" name="CountryId" asp-for="@Model.CascadingModel.CountryId" asp-items="@Model.CascadingModel.Countries">
            <option value="">Please select</option>
        </select>
        <br/><br/>
        <select id="ddlStates" name="StateId" asp-for="@Model.CascadingModel.StateId" asp-items="@Model.CascadingModel.States">
            <option value="">Please select</option>
        </select>
        <br/><br/>
        <select id="ddlCities" name="CityId" asp-for="@Model.CascadingModel.CityId" asp-items="@Model.CascadingModel.Cities">
            <option value="">Please select</option>
        </select>
        <br/><br/>
        <input type="submit" value="Submit" asp-page-handler="Submit" />
    </form>
    <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
    <script type="text/javascript">
        $(function () {
            $("select").each(function () {
                if ($(this).find("option").length <= 1) {
                    $(this).attr("disabled", "disabled");
                }
            });
 
            $("select").change(function () {
                var value = 0;
                if ($(this).val() != "") {
                    value = $(this).val();
                }
                var id = $(this).attr("id");
                $.ajax({
                    type: "POST",
                    url: "/Index?handler=AjaxMethod",
                    beforeSend: function (xhr) {
                        xhr.setRequestHeader("XSRF-TOKEN",
                            $('input:hidden[name="__RequestVerificationToken"]').val());
                    },
                    data: { value: value, type: id },
                    success: function (response) {
                        switch (id) {
                            case"ddlCountries":
                                DisableDropDown("#ddlStates");
                                DisableDropDown("#ddlCities");
                                PopulateDropDown("#ddlStates", response.States);
                                break;
                            case"ddlStates":
                                DisableDropDown("#ddlCities");
                                PopulateDropDown("#ddlCities", response.Cities);
                                break;
                        }
                    },
                    failure: function (response) {
                        alert(response.responseText);
                    },
                    error: function (response) {
                        alert(response.responseText);
                    }
                });
            });
 
            if ($("#ddlCountries").val() != "" && $("#ddlStates").val() != "" && $("#ddlCities").val() != "") {
                var message = "Country: " + $("#ddlCountries option:selected").text();
                message += "\nState: " + $("#ddlStates option:selected").text();
                message += "\nCity: " + $("#ddlCities option:selected").text();
                alert(message);
            }
        });
 
        function DisableDropDown(dropDownId) {
            $(dropDownId).attr("disabled", "disabled");
            $(dropDownId).empty().append('<option selected="selected" value="0">Please select</option>');
        }
 
        function PopulateDropDown(dropDownId, list) {
            if (list != null && list.length > 0) {
                $(dropDownId).removeAttr("disabled");
                $.each(list, function () {
                    $(dropDownId).append($("<option></option>").val(this['Value']).html(this['Text']));
                });
            }
        }
    </script>
</body>
</html>
 
 
Screenshot
ASP.Net Core Razor Pages: Cascading (Dependent) Country State City DropDownLists using jQuery AJAX
 
 
Browser Compatibility
The above code has been tested in the following browsers.

Internet Explorer  FireFox  Chrome  Safari  Opera 

* All browser logos displayed above are property of their respective owners.

 
 
Downloads