Monday, December 11, 2023

Mix Blazor with ASP.NET Core MVC

In this tutorial I will show you how you can inject Blazor components into your regular ASP.NET Core MVC web application. We will integrate the free QuickGrid Blazor components into our MVC application. 

Source code: https://github.com/medhatelmasry/MvcWithBlazor

Companion video: https://youtu.be/dsoGW59Vtpk

Prerequisites

It is assumed that you have installed the following on your computer:
  • .NET 8.0
  • Visual Studio Code editor

Getting Started

In a suitable working directory, create an ASP.NET MVC 8.0 web application with the following terminal window command:

dotnet new mvc -f net8.0 --name MvcWithBlazor

Change into the new directory with:

cd MvcWithBlazor

Add the following Blazor QuickGrid package with this terminal window command:

dotnet add package Microsoft.AspNetCore.Components.QuickGrid -v 8.0.0

Start Visual Studio Code with:

code .

To keep it simple, we will create some hard-coded student data. Create a Student.cs class file inside the Models folder with the following code:

namespace MvcWithBlazor.Models;
public class Student
{
    required public int? Id { get; set; }
    required public string FirstName { get; set; }
    required public string LastName { get; set; }
    required public string School { get; set; }
    public static IQueryable<Student> GetStudents()
    {
        int ndx = 0;
        List<Student> students = new List<Student>() {
            new Student() { Id = ++ndx, FirstName="Max", LastName="Pao", School="Science" },
            new Student() { Id = ++ndx, FirstName="Tom", LastName="Fay", School="Mining" },
            new Student() { Id = ++ndx, FirstName="Ann", LastName="Sun", School="Nursing" },
            new Student() { Id = ++ndx, FirstName="Joe", LastName="Fox", School="Computing" },
            new Student() { Id = ++ndx, FirstName="Sue", LastName="Mai", School="Mining" },
            new Student() { Id = ++ndx, FirstName="Ben", LastName="Lau", School="Business" },
            new Student() { Id = ++ndx, FirstName="Zoe", LastName="Ray", School="Mining" },
            new Student() { Id = ++ndx, FirstName="Sam", LastName="Ash", School="Medicine" },
            new Student() { Id = ++ndx, FirstName="Dan", LastName="Lee", School="Computing" },
            new Student() { Id = ++ndx, FirstName="Pat", LastName="Day", School="Science" },
            new Student() { Id = ++ndx, FirstName="Kim", LastName="Rex", School="Computing" },
            new Student() { Id = ++ndx, FirstName="Tim", LastName="Ram", School="Business" },
            new Student() { Id = ++ndx, FirstName="Rob", LastName="Wei", School="Mining" },
            new Student() { Id = ++ndx, FirstName="Jan", LastName="Tex", School="Science" },
            new Student() { Id = ++ndx, FirstName="Jim", LastName="Kid", School="Business" },
            new Student() { Id = ++ndx, FirstName="Ben", LastName="Chu", School="Medicine" },
            new Student() { Id = ++ndx, FirstName="Mia", LastName="Tao", School="Computing" },
            new Student() { Id = ++ndx, FirstName="Ted", LastName="Day", School="Business" },
            new Student() { Id = ++ndx, FirstName="Amy", LastName="Roy", School="Science" },
            new Student() { Id = ++ndx, FirstName="Ian", LastName="Kit", School="Nursing" },
            new Student() { Id = ++ndx, FirstName="Liz", LastName="Tan", School="Medicine" },
            new Student() { Id = ++ndx, FirstName="Mat", LastName="Roy", School="Tourism" },
            new Student() { Id = ++ndx, FirstName="Deb", LastName="Luo", School="Medicine" },
            new Student() { Id = ++ndx, FirstName="Ana", LastName="Poe", School="Computing" },
            new Student() { Id = ++ndx, FirstName="Lyn", LastName="Raj", School="Science" },
            new Student() { Id = ++ndx, FirstName="Amy", LastName="Ash", School="Tourism" },
            new Student() { Id = ++ndx, FirstName="Kim", LastName="Kid", School="Mining" },
            new Student() { Id = ++ndx, FirstName="Bec", LastName="Fry", School="Nursing" },
            new Student() { Id = ++ndx, FirstName="Eva", LastName="Lap", School="Computing" },
            new Student() { Id = ++ndx, FirstName="Eli", LastName="Yim", School="Business" },
            new Student() { Id = ++ndx, FirstName="Sam", LastName="Hui", School="Science" },
            new Student() { Id = ++ndx, FirstName="Joe", LastName="Jin", School="Mining" },
            new Student() { Id = ++ndx, FirstName="Liz", LastName="Kuo", School="Agriculture" },
            new Student() { Id = ++ndx, FirstName="Ric", LastName="Mak", School="Tourism" },
            new Student() { Id = ++ndx, FirstName="Pam", LastName="Day", School="Computing" },
            new Student() { Id = ++ndx, FirstName="Stu", LastName="Gad", School="Business" },
            new Student() { Id = ++ndx, FirstName="Tom", LastName="Bee", School="Tourism" },
            new Student() { Id = ++ndx, FirstName="Bob", LastName="Lam", School="Agriculture" },
            new Student() { Id = ++ndx, FirstName="Jim", LastName="Ots", School="Medicine" },
            new Student() { Id = ++ndx, FirstName="Tom", LastName="Mag", School="Mining" },
            new Student() { Id = ++ndx, FirstName="Hal", LastName="Doe", School="Agriculture" },
            new Student() { Id = ++ndx, FirstName="Roy", LastName="Kim", School="Nursing" },
            new Student() { Id = ++ndx, FirstName="Vis", LastName="Cox", School="Science" },
            new Student() { Id = ++ndx, FirstName="Kay", LastName="Aga", School="Tourism" },
            new Student() { Id = ++ndx, FirstName="Reo", LastName="Hui", School="Business" },
            new Student() { Id = ++ndx, FirstName="Bob", LastName="Roe", School="Medicine" },
        };
        return students.AsQueryable();
    }
}

Create a Components folder and add to it the following files with their respective contents:

_Imports.razor

@using System.Net.Http
@using System.Net.Http.Json
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.Web
@using static Microsoft.AspNetCore.Components.Web.RenderMode
@using Microsoft.AspNetCore.Components.Web.Virtualization
@using Microsoft.JSInterop
@using MvcWithBlazor
@using MvcWithBlazor.Components 
@using Microsoft.AspNetCore.Components.QuickGrid
@using MvcWithBlazor.Models

Routes.razor

<Router AppAssembly="@typeof(Program).Assembly">
    <Found Context="routeData">
        <RouteView RouteData="@routeData" />
        <FocusOnNavigate RouteData="@routeData" Selector="h1" />
    </Found>
</Router>

App.razor

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <base href="/" />
    <link rel="stylesheet" href="MvcWithBlazor.styles.css" />
    <HeadOutlet />
</head>
<body>
    <Routes />
    <script src="_framework/blazor.web.js"></script>
</body>
</html>

Note that you need adjust the above highlighted text with your application name.

Inside the Components folder, add a sub-folder named Pages. Inside the Components/Pages folder add a Students.razor component file with this code:

@page "/students"
@rendermode InteractiveServer
<PageTitle>Students</PageTitle>
<h1>Students</h1>
<QuickGrid Items="@students" Pagination="@pagination">
    <PropertyColumn Property="@(_ => _.Id)" Sortable="true" />
    <TemplateColumn Title="Name" SortBy="@sortByName">
        <div class="flex items-center">
            <nobr>
                <strong>@context.FirstName @context.LastName</strong>
            </nobr>
        </div>
    </TemplateColumn>
    <PropertyColumn Property="@(_ => _.School)" Sortable="true" />
</QuickGrid>
<Paginator State="@pagination" />

@code {
    IQueryable<Student> students = Student.GetStudents();
    PaginationState pagination = new PaginationState { ItemsPerPage = 10 };
    GridSort<Student> sortByName = GridSort<Student>
    .ByAscending(_ => _.FirstName).ThenAscending(_ => _.LastName);
}

Adding appropriate services and middleware to Program.cs

Add the following using statement at the top of Program.cs:

using MvcWithBlazor.Components;

Add the following razor component services before the line that calls builder.Build():

builder.Services
.AddRazorComponents()
.AddInteractiveServerComponents()
.AddCircuitOptions(options => options.DetailedErrors = true); // for debugging razor components

Add this anti-forgery middleware right after 'app.UseRouting();':

app.UseAntiforgery();

Place this code before the line that calls app.Run():

app.MapRazorComponents<App>()
.AddInteractiveServerRenderMode();

Try it out

Run the application with the following terminal window command:

dotnet watch

When the home page loads in your browser, try the /students page. You should see a page that looks like this:


You can sort any column by clicking on the column title and the paging should also work. We have succeeded in mixing Blazor with ASP.NET MVC. 

Injecting Blazor components into MVC views

We can inject Blazor components directly into MVC .cshtml views. We must first make the appropriate namespaces visible to the environment for the MVC views. Add the following using statements to Views/_ViewImports.cshtml:

@using MvcWithBlazor.Components
@using MvcWithBlazor.Components.Pages

Note that you must adjust the above highlighted text with the appropriate name of your application.

Add the following <base> tag and Component Tag Helper for a HeadOutlet component to the <head> markup of Views/Shared/_Layout.cshtml:

<base href="~/" />
<component type="typeof(Microsoft.AspNetCore.Components.Web.HeadOutlet)" 
    render-mode="ServerPrerendered" />

The HeadOutlet component is used to render head (<head>) content for page titles (PageTitle component) and other head elements (HeadContent component) set by Razor components.

Also, in the same Views/Shared/_Layout.cshtml file, add a <script> tag for the blazor.web.js script immediately before the Scripts render section (@await RenderSectionAsync(...)):

<script src="_framework/blazor.web.js"></script>

Next, add the following tag to the bottom of home page Views/Home/Index.cshtml:

<component type="typeof(Students)" render-mode="ServerPrerendered" />

Point your browser to the home page of the ASP.NET Core MVC app. You should experience a page that has the Students component embedded inside it.

Paging and sorting work as expected.

Conclusion

You can do the same with ASP.NET Core Razor Pages.

I hope you found this article useful.

No comments:

Post a Comment