Getting Started with MudBlazor

Getting Started with MudBlazor

GitHub repository

Introduction

It’s been a while since I last wrote a development post! As you might guess, work has kept me busy, and the past year brought some family challenges as well. But now, I'm back and excited to dive into an interesting topic: Blazor WASM.

A good friend of mine, Oscar Agreda, introduced me to MudBlazor, an ambitious project bringing Material Design to Blazor. So, in this post, we’ll be exploring MudBlazor together.

We can start with the Getting Started - Installation guide, which provides a solid foundation. However, my goal here is to go beyond just the basics of MudBlazor and explore its potential in depth.

Prerequisites

Software

  • .NET Core SDK
  • Visual Studio 2022 (last update)

Install MudBlazor templates with the following command line:

dotnet new install MudBlazor.Templates

Skills

  • C#

Code factory

  1. Create project
  2. Running project
  3. Add service
  4. Add Blazor component
  5. Setting up dependency injection
  6. Updating project

Create project

Select Blazor and go to the end, then select MudBlazor Web App (MudBlazor) item:


We'll use the MudBlazorWebApp1 name for project, but You have the freedom of using another name.


Use all default options for project:


In a MudBlazor project, having separate server and client projects is typically seen in Blazor WebAssembly (WASM) applications when using an ASP.NET Core hosted setup. Here's the purpose of each:

  • Client Project: This contains the Blazor WebAssembly app that runs in the browser. It's responsible for rendering the UI and handling client-side logic. Since it's a WebAssembly app, it executes directly on the client’s machine and can function offline after the initial load.
  • Server Project: This is an ASP.NET Core application that serves as the backend. It hosts the WebAssembly app (client) and can provide additional server-side capabilities like APIs, authentication, and data storage. This setup allows the server project to manage database access and other server-side resources securely.

By separating the client and server, you get a clear division between frontend and backend, improving scalability, security, and performance.


Running project

Before of running project, edit launchSettings.json file, We'll only need one profile for this guide, edit as follows:


This is the MudBlazor project running on 7000 port:


As usual, the project template includes a layout with a side menu:


Add service

We'll add a service and call it within a Blazor component, demonstrating how dependency injection works in a Blazor WASM project.

In the Client project, create a new directory named Services. Then, add a class called PistachioStoreService inside the Services directory.


using MudBlazorWebApp1.Client.Services.Models;

namespace MudBlazorWebApp1.Client.Services;

public class PistachioStoreService
{
    public async Task> GetProductsAsync()
    {
        return await Task.FromResult(new List
        {
            new(1, "The King of Fighters XV", "Fighting games", "PlayStation", 49.99m, new DateTime(2022, 2, 17)),
            new(2, "Street Fighter VI", "Fighting games", "PlayStation", 59.99m, new DateTime(2023, 6, 2)),
            new(3, "Tekken 8", "Fighting games", "Xbox", 59.99m, new DateTime(2024, 1, 26)), // Expected release date
            new(4, "Garou: City of the Wolves", "Fighting games", "PC", 59.99m, new DateTime(2024, 12, 15)), // Expected release date
            new(5, "Mortal Kombat 1", "Fighting games", "PlayStation", 69.99m, new DateTime(2023, 9, 14)),
            new(6, "Soulcalibur VI", "Fighting games", "PC", 39.99m, new DateTime(2018, 10, 19)),
            new(7, "Guilty Gear -Strive-", "Fighting games", "PlayStation", 49.99m, new DateTime(2021, 6, 11)),
            new(8, "Dragon Ball FighterZ", "Fighting games", "Xbox", 39.99m, new DateTime(2018, 1, 26)),
            new(9, "BlazBlue: Central Fiction", "Fighting games", "PC", 29.99m, new DateTime(2015, 11, 19)),
            new(10, "Injustice 2", "Fighting games", "Xbox", 49.99m, new DateTime(2017, 5, 16)),
            new(11, "Samurai Shodown", "Fighting games", "PlayStation", 39.99m, new DateTime(2019, 6, 25)),
            new(12, "Dead or Alive 6", "Fighting games", "PC", 29.99m, new DateTime(2019, 3, 1)),
            new(13, "Virtua Fighter 5: Ultimate Showdown", "Fighting games", "PlayStation", 19.99m, new DateTime(2021, 6, 1)),
            new(14, "Power Rangers: Battle for the Grid", "Fighting games", "Xbox", 29.99m, new DateTime(2019, 3, 26)),
            new(15, "Melty Blood: Type Lumina", "Fighting games", "PC", 49.99m, new DateTime(2021, 9, 30)),
            new(16, "Brawlhalla", "Fighting games", "PC", 0.00m, new DateTime(2017, 10, 17)),  // Free-to-play
            new(17, "Super Smash Bros. Ultimate", "Fighting games", "Nintendo Switch", 59.99m, new DateTime(2018, 12, 7)),
            new(18, "Under Night In-Birth Exe:Late[cl-r]", "Fighting games", "PlayStation", 34.99m, new DateTime(2020, 2, 20)),
            new(19, "Granblue Fantasy: Versus", "Fighting games", "PlayStation", 29.99m, new DateTime(2020, 3, 3)),
            new(20, "Rivals of Aether", "Fighting games", "PC", 14.99m, new DateTime(2017, 3, 28)),
        });
    }
}    

Now add a file with ProductListModel name inside of Services\Models directory.


namespace MudBlazorWebApp1.Client.Services.Models;

public record ProductListModel
{
    public ProductListModel()
    {
    }

    public ProductListModel(int id, string? name, string? category, string? platform, decimal unitPrice, DateTime releaseDate)
    {
        Id = id;
        Name = name;
        Category = category;
        Platform = platform;
        UnitPrice = unitPrice;
        ReleaseDate = releaseDate;
    }

    public int Id { get; set; }
    public string? Name { get; set; }
    public string? Category { get; set; }
    public string? Platform { get; set; }
    public decimal UnitPrice { get; set; }
    public DateTime ReleaseDate { get; set; }
}    

Add Blazor component

Now add a Blazor component in Client project:


Then add a new item:


Edit Layout\NavMenu.razor file to add a link for ProductList Blazor component:


Now, add the following code for ProductList Blazor component:


@page "/product"

@using MudBlazorWebApp1.Client.Services
@using MudBlazorWebApp1.Client.Services.Models

@inject PistachioStoreService pistachioStoreService

<PageTitle>Products</PageTitle>

<MudText Typo="Typo.h3" GutterBottom="true">Products</MudText>

<MudButton id="loadDataButton" Variant="Variant.Filled" Color="Color.Primary" @onclick="LoadDataAsync">Load Data</MudButton>

<br />

@if (Model == null)
{
    <br />
    <MudText Typo="Typo.h5" GutterBottom="true">There is no data.</MudText>
}
else
{
    <br />

    <MudText Id="recordsLabel">@RecordsLabel</MudText>

    <MudTable Items="Model" Hover="true" SortLabel="Sort By" Elevation="0" AllowUnsorted="false">
        <HeaderContent>
            <MudTh><MudTableSortLabel InitialDirection="SortDirection.Ascending" SortBy="new Func<ProductListModel, string?>(item=>item.Name)">Name</MudTableSortLabel></MudTh>
            <MudTh><MudTableSortLabel InitialDirection="SortDirection.Ascending" SortBy="new Func<ProductListModel, string?>(item=>item.Category)">Category</MudTableSortLabel></MudTh>
            <MudTh><MudTableSortLabel InitialDirection="SortDirection.Ascending" SortBy="new Func<ProductListModel, string?>(item=>item.Platform)">Platform</MudTableSortLabel></MudTh>
            <MudTh><MudTableSortLabel InitialDirection="SortDirection.Ascending" SortBy="new Func<ProductListModel, object>(item=>item.UnitPrice)">Unit price</MudTableSortLabel></MudTh>
            <MudTh><MudTableSortLabel InitialDirection="SortDirection.Ascending" SortBy="new Func<ProductListModel, object>(item=>item.ReleaseDate)">Release date</MudTableSortLabel></MudTh>
        </HeaderContent>
        <RowTemplate>
            <MudTd DataLabel="Name">@context.Name</MudTd>
            <MudTd DataLabel="Category">@context.Category</MudTd>
            <MudTd DataLabel="Platorm">@context.Platform</MudTd>
            <MudTd DataLabel="UnitPrice">@context.UnitPrice.ToString("C2")</MudTd>
            <MudTd DataLabel="ReleaseDate">@context.ReleaseDate.ToShortDateString()</MudTd>
        </RowTemplate>
        <PagerContent>
            <MudTablePager PageSizeOptions="new int[]{50, 100}" />
        </PagerContent>
    </MudTable>
}

@code {
    private List<ProductListModel>? Model { get; set; }
    private string? RecordsLabel { get; set; }

    protected override async Task OnInitializedAsync()
    {
    }

    async Task LoadDataAsync()
    {
        Model = await pistachioStoreService.GetProductsAsync();
        RecordsLabel = $"Total of records: {Model?.Count}";
    }
}

The file structure would be like follows:


Setting up dependency injection

We'll inject PistachioStoreService into a Blazor component, so we need to add it to the services collection:


using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
using MudBlazor.Services;
using MudBlazorWebApp1.Client.Services;

var builder = WebAssemblyHostBuilder.CreateDefault(args);

builder.Services.AddScoped<PistachioStoreService>();

builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
builder.Services.AddMudServices();

await builder.Build().RunAsync();    

There is no connection with any API, so We don't need to add configuration binding to resolve PistachioStoreService class.

Updating project

Save all changes and run the project, then click on side menu icon:




Code Challenge

If you’re looking to deepen your understanding of MudBlazor, try the following challenge to sharpen your skills:

  • Add a text box that allows searching by any string property in the model, such as name, category, or platform, with case-insensitive matching.
  • Add additional properties to the ProductListModel record, and include them in the row template.
  • Create a new model for the service, add another list component, and load data using a new method.

Wrapping Up

As we’ve seen, MudBlazor integrates smoothly with Blazor and provides an experience comparable to Razor Pages. It follows an MVVM-like approach and supports two-way binding expressions.

The @inject keyword lets us inject a service into a Blazor component.

For additional components, explore the MudBlazor official site. For example, see the Table component.

Code Improvements

This code demonstrates MudBlazor basics, but there’s always room for improvement! Here are a few enhancements to consider:

  • Create a dedicated directory for the Products feature, then organize all related Blazor components by feature.
  • Add a Progress component to display while data is loading.
  • Refactor the PistachioStoreService class to implement an interface.

Points of Interest

Blazor components don’t use a traditional class structure, so they lack constructors and code-behind files; the @code { ... } section contains all C# code directly.

We registered the service in the Client project. Keep in mind that the Server project references the Client project. If we need to retrieve values from appsettings.json, we must add them to the JSON file located in the wwwroot directory of the Client project.

Extension methods can be used in Blazor components—just add the necessary using directive at the top of the file.

Further reading

Change history

  1. 13th November, 2024: Initial version

Comments

Popular Posts