Blazor .NET 8 - Enhanced Form Navigation

22/11/2023

There are many new cool features with .NET 8 and Blazor in particular. In this blog post, I want to highlight a feature that I believe is very useful in the new context Blazor is living.

Blazor Forms and Navigation

To wrap up why Blazor is now a big deal (and probably bigger than ever): Server Side Rendering and the ability to just deliver static content! I briefly summarize from my own blog post article from a few months ago:

Now meet server-side rendering: This is kind of a special case of the server hosting model. Here, the server renders the whole DOM and sends it to the client. The client only renders the DOM. That is it. No SignalR, just static content. That is amazing for read-only content with zero interactions, but obviously falls short if you need to handle button clicks.

So if you have a read-only site - that is a game changer for you. But wait - let's examine my last sentence: but obviously falls short if you need to handle button clicks. That is not true! Here is where Enhanced Form Navigation comes into play. Blazor gives you the ability to handle forms in a "SPA" manner without utilizing either InteractiveServer or InteractiveWebAssembly mode. This can be a huge deal! Let's see how it works.

Example

For our example, I use the default template for Blazor shipped with .NET 8, but one big adjustment: I disable any interactiity mode.

builder.Services.AddRazorComponents();

...

app.MapRazorComponents<App>();

No interactivity, as you can see. Now let's create a simple page that holds some recipes, ingredients or what not and a form element that we will utilize as an input:

@page "/"

<form data-enhance>
    <input name="query" placeholder="Search" value="@Query" />
    <button>Search</button>
</form>

<ul>
    @foreach (var item in _filteredEntries)
    {
        <li>@item</li>
    }
</ul>

@code {
    [SupplyParameterFromQuery]
    public string Query { get; set; }

    public List<string> _entries = ["Chocolate", "Vanilla", "Strawberry"];
    private List<string> _filteredEntries;

    protected override void OnInitialized()
    {
        _filteredEntries = !string.IsNullOrEmpty(Query) 
            ? _entries.Where(x => x.Contains(Query)).ToList() 
            : _entries;
    }
}

Two major things here: We have a Query-Parameter that gets filled via the SupplyParameterFromQuery-Attribute. This is a feature that allows you to supply parameters from the query string. The second thing is the form-element. It has a data-enhance-attribute. This is the magic that allows us to use the form in a SPA-like manner. Let's see what happens if we "search" something:

Video

Keep in mind, that we use the same name in the form/input as the query string - so there is no big magic happening here. We have a form that does a request. As you can see in the video, blazor.web.js does make a fetch request and dynamically patches the DOM! There is no additional content downloaded - or in the worst case, the whole page again!

Resources

  • Source code to this blog post: here
  • All my sample code is hosted in this repository: here

.NET 8 and Blazor United / Server-side rendering

New .NET and new Blazor features. In this blog post, I want to highlight the new features that are hitting us with .NET 8 in the Blazor world. So let's see what's new.

Blazor Project Structure

Did you ever wonder what is a nice way of structuring your Blazor application?

I will show you how I structure my Blazor projects (as well as this very blog). What are the upside in contrast to the "default" structuring you get with the Blazor template.

RequiredIf - Extend the validation in Blazor

Just imagine you have your beautiful model in a form. And Blazor is very kind and does validation for you. It is really straight forward because you can annotate your model with some attributes like RequiredAttribute to tell Blazor: "Hey this property has to be set, otherwise the form will not be submitted."

But what if you want to have that easy setup with attributes but also want to say to Blazor: "Hey this property has to be set, if the other property is true.* Well, that does not work out of the box. So let's make it together!

An error has occurred. This application may no longer respond until reloaded. Reload x