Partial Page Update with AJAX in Razor Pages

Partial Pages consist of fragments of HTML and server-side code to be included in any number of pages or layouts. Partial pages can be used to break up complex pages into smaller units, thereby reducing the complexity and allowing teams to work on different units concurrently. They can also be used to provide content for updating part of the rendered web page via AJAX in client side script.

A partial page is a single .cshtml file. It has no PageModel and it doesn't have an @page directive at the top. A partial can be strongly typed - have an @model directive, or it can work purely with ViewData. For the purposes of this article, I will be using a strongly typed partial.

The example features the same ICarService used in Working with JSON in Razor Pages. A collection of Car objects will be passed to the partial for display, which will be invoked via client-side script:

First, the Car entity:

public class Car
{
    public int Id { get; set; }
    public string Make { get; set; }
    public string Model { get; set; }
    public int Year { get; set; }
    public int Doors { get; set; }
    public string Colour { get; set; }
    public decimal Price { get; set; }
}

The service has one method that conforms to an interface, generating and returning a list of cars:

public interface ICarService
{
    List<Car> GetAll();
}

public class CarService : ICarService
{
    public List<Car> GetAll()
    {
        List<Car> cars = new List<Car>{
            new Car{Id = 1, Make="Audi",Model="R8",Year=2014,Doors=2,Colour="Red",Price=79995},
            new Car{Id = 2, Make="Aston Martin",Model="Rapide",Year=2010,Doors=2,Colour="Black",Price=54995},
            new Car{Id = 3, Make="Porsche",Model=" 911 991",Year=2016,Doors=2,Colour="White",Price=155000},
            new Car{Id = 4, Make="Mercedes-Benz",Model="GLE 63S",Year=2017,Doors=5,Colour="Blue",Price=83995},
            new Car{Id = 5, Make="BMW",Model="X6 M",Year=2016,Doors=5,Colour="Silver",Price=62995},
        };
        return cars;
    }
}

This service is registered with the ASP.NET Core Dependency Injection system so that it can be made available throughout the application:

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();
    services.AddTransient<ICarService, CarService>();
}

The partial itself is contains an HTML table styled using Bootstrap. It is named _CarPartial.cshtml and is placed in the Pages/Shared folder - that being one of the default search locations for partials in Razor Pages. The model that the partial expects is a List<Car>:

@model List<Car>

<table class="table table-striped">
@foreach(var car in Model)
{
    <tr>
        <td>@car.Model</td>
        <td>@car.Make</td>
        <td>@car.Year</td>
        <td>$@car.Price</td>
    </tr>
}
</table>

Most often, the handler method that serves up partials will not be the primary handler method in a specific page. Instead, a named handler will be used. The following code shows a basic PageModel that includes a named handler (OnGetCarPartial) returning a PartialViewResult:

public class AjaxPartialModel : PageModel
{
    private ICarService _carService;
    public AjaxPartialModel(ICarService carService)
    {
        _carService = carService;
    }

    public List<Car> Cars { get; set; }
    public void OnGet()
    {
    }

    public PartialViewResult OnGetCarPartial()
    {
        Cars = _carService.GetAll();
        return new PartialViewResult {
            ViewName = "_CarPartial",
            ViewData = new ViewDataDictionary<List<Car>>(ViewData, Cars)
        };
    }
}

In Razor Pages versions up to and including 2.1, you must explicitly create an instance of PartialViewResult and return that from the handler, passing in the current ViewData dictionary and, optionally, a model for the partial (if it expects one).

Here is the content page where jQuery is used to make the AJAX request when the button is clicked:

@page
@model RazorPagesTests.Pages.AjaxPartialModel


<h2>Ajax</h2>
<p><button class="btn btn-primary" id="load">Load</button></p>
<div id="grid"></div>

@section scripts{
    <script>
        $(function () {
            $('#load').on('click', function () {
                $('#grid').load('/ajaxpartial?handler=CarPartial');
            });
        });
    </script>
}

Notice particularly the URL that is passed to the load method - the name of the named handler method (without the OnGet part) is passed to a querystring value calledhandler`. If you prefer not to have a query string, you can create a route template in the Razor Page that adds an optional item named handler to the route values:

@page "{handler?}"

This enables you to can pass the name of the handler as a segment of the URL:

$('#grid').load('/ajaxpartial/carpartial');

Here is an alternative version that uses the Fetch API instead of the XMLHttpRequest object via jQuery:

@page
@model RazorPagesTests.Pages.AjaxPartialModel


<h2>Ajax</h2>
<p><button class="btn btn-primary" id="load">Load</button></p>
<div id="grid"></div>

@section scripts{
    <script>
        document.getElementById('load').addEventListener('click', () => {
            fetch('/ajaxpartial?handler=CarPartial')
                .then((response) => {
                    return response.text();
                })
                .then((result) => {
                    document.getElementById('grid').innerHTML = result;
                });
        });
    </script>
}

The boilerplate code required to construct the PartialViewResult will no longer be necessary from ASP.NET Core 2.2 (due to RTM later during 2018), because the PageModel class will be getting a Partial helper method just like controllers in MVC. At that point, the current OnGetCarPartial method can be changed to the following:

public PartialViewResult OnGetCarPartial()
{
    Cars = _carService.GetAll();
    return Partial("_CarPartial", Cars);
}
Last updated: 19/09/2018 07:45:22

© 2018 - Learn Razor Pages.
All rights reserved.