The new ASP.NET Core Razor Pages project continues, and with it comes my team's unending need to do things we haven't done before in this architecture.

Like maybe take a vacation? Photo by Tommy Lisbin / Unsplash

This time, we wanted a way to build a cascading drop down list using Razor Pages and jQuery, and after a little bit of trial and error settled on this method of doing so. Come along with me as we see how to wire up a jQuery AJAX request in ASP.NET Core Razor Pages using a new feature, termed Named Handler Methods. Let's go!

The Sample Project

As always with my code-focused posts, there is a sample project for this post over on GitHub. Check it out!

exceptionnotfound/RazorPagesAJAXNamedHandlersDemo
Contribute to exceptionnotfound/RazorPagesAJAXNamedHandlersDemo development by creating an account on GitHub.

The Setup

We wanted to build a cascading drop down list, that is, a set of select lists where the first list's selected value determines the options shown in the second list.

For this demo, let's say we want a list of continents, and to have a second list of countries for the selected continent. In other words, the page might look like this:

Now we need the selected value in the first drop down "Continent" to change the available values in the second drop down "Country".

Let's also imagine that we have the following repository interface in our data access layer:

public interface ILocationRepository
{
    List<string> GetContinents();
    List<string> GetCountries(string continent);
}
Implementation details are over on GitHub. Be warned: they are not very fancy.

So we already have the ability to get a list of countries for a specific continent. What we are going to do now is use Razor Pages, and specifically the Named Handler Methods feature, to wire up an AJAX request to get the countries for a specified continent.

Named Handler Methods

We already know from previous posts about ASP.NET Core Razor Pages that each page supports two methods by default: OnGet() and OnPost(). These methods are often the "normal" way of allowing a page to GET and POST data to and from the server.

In fact, Razor Pages supports other method formats as well. For example, this would be a supported method name:

public IActionResult OnGetListContinents() { }

The key part is the OnGet prefix; this tells ASP.NET Core that this method is expected to respond to a GET request. Please note that any parameters to these methods do not distinguish them as far as ASP.NET Core is concerned, so having the methods OnGetListCountries() and OnGetListCountries(string continent) on the same page will cause an InvalidOperationException to be thrown when the methods are invoked.

This syntax is called Named Handler Methods, and you can read more about their implementation on the official docs site.

Introduction to Razor Pages in ASP.NET Core
Learn how Razor Pages in ASP.NET Core makes coding page-focused scenarios easier and more productive than using MVC.

Creating an AJAX Request with Named Handler Methods

Now for the real meat of this post: given that we can use Named Handler Methods in Razor Pages and that we have a data access layer, how can we create an AJAX request to get the countries for a given continent?

First, we need to create the named handler method itself. It looks like this:

public JsonResult OnGetCountriesFilter(string continent)
{
    return new JsonResult(_locationRepo.GetCountries(continent));
}

What this will do is return a JSON collection of countries for the given continent. The rest of the work will be done in JavaScript and jQuery.

First, let's have the call be executed whenever the select list value changes (via the .change() function):

$("#continentsList").change(function () {
    var continent = $("#continentsList").val();
    //Make AJAX call here
});

We need a call to jQuery's $.ajax() method, and that method must point at our named handler method from earlier. Such a call might look like this:

$.ajax({
    url: "@Url.Page("/Index")?handler=CountriesFilter",
    method: "GET",
    data: { continent: continent },
    success: function (data) {
        //TODO
    }
})

Note the call to @Url.Page(). This code is using the IUrlHelper interface to generate the URL to the /Index page, the page we are running this script on. We could also not use that call at all, in which case the invocation would be this:

$.ajax({
    url: "?handler=CountriesFilter",
    method: "GET",
    data: { continent: continent },
    success: function (data) {
        //TODO
    }
})

However my "official" solution includes the call to IUrlHelper because such a call is going to work on many different pages, not just the ones that map to the default URL of "~/".

The last thing to do is to fill out the success() function. We need to remove all existing items in the Countries select list and repalce them with the values retrieved during the AJAX call. We can do that like this:

$.ajax({
    //...
    success: function (data) {
        //Remove all items in the countriesList
        $("#countriesList option").remove();
        
        //For each item retrieved in the AJAX call...
        $.each(data, function (index, itemData) {
            //...append that item to the countriesList
            $("#countriesList").append("<option value='" + itemData + "'>" + itemData + "</option>");
        });
    }
})

The Final Script

With all of the above in place, the final AJAX script looks like this:

$("#continentsList").change(function () {
    var continent = $("#continentsList").val();
    $.ajax({
        url: "@Url.Page("/Index")?handler=CountriesFilter",
        method: "GET",
        data: { continent: continent },
        success: function (data) {
            //Remove all items in the countriesList
            $("#countriesList option").remove();

            //For each item retrieved in the AJAX call...
            $.each(data, function (index, itemData) {
                //...append that item to the countriesList
                $("#countriesList").append("<option value='" + itemData + "'>" + itemData + "</option>");
            });
        }
    })
});
I have no doubt that this could be simplified or shortened.

We're done! Now we have a cascading drop down list for continents and countries, all using Named Handler Methods and jQuery Ajax.

Summary

In ASP.NET Core Razor Pages, we can wire up jQuery AJAX calls with server-side Named Handler Methods to provide a way to query for data without needing to POST the entire form or page. In this way, we can build nice cascading drop down lists, and other user-friendly controls.

Don't forget to check out the sample project for this post over on GitHub!

Happy Coding!