Creating a fully interactive client frontend WITHOUT JavaScript

Mylocloud Limited | Ryan Barlow | Technical Architect services - specialising in .NET, Azure, JS (Vanilla, TypeScript, React, Express)
East Midlands UK, Remote, Hybrid

Find us on LinkedIn at;
www.linkedin.com/in/ryan-barlow-uk and www.linkedin.com/company/mylocloud

Are you a .NET dev who wants to avoid JavaScript? Has your client asked you to work on a front end? Then today's topic may be just the thing for you...

Fig.1 - The above is how Microsoft market it

Introducing Blazor

Blazor is a framework that allows all interactive logic of a web application (front end + back end) to be coded in C#.

It is an open source ASP.NET Core framework, leveraging WebAssembly to work as an alternative to the common JS + AJAX / Websockets server (ASP.NET Core, Express etc..) stack. It has found some level of popularirty in enterprise applications.

The concept of creating a framework to allow experiened C# developers to transfer their skills to front end websites, calls back 20 years, to when Microsfot introduced ASP.NET Webforms - allowing a generation of WinForms developers to quickly develop websites.

Example

In the code below;

<h1>Counter</h1>

<p>Count: @count</p>

<button @onclick="Increment">
    Increment</button>

@code 
{
    private int count = 0;

    private void Increment()
    {
        count++;
    }
}

Getting started

Now that you have read a little about what Blazor is, and viewed some simple example code - lets get started!

Install the .NET 8 SDK

First make sure you have the .NET 8 SDK downloaded and installed from https://dotnet.microsoft.com/en-us/download/dotnet/8.0

Fig.2 - SDK download page. If on M-series Mac, you want to choose 'macOS Arm64'

Create new Blazor Web App project

Go to the folder you want the new solution created in, and then in your terminal (command line in Windows), run the following;

dotnet new blazor -o BlazorBlogExample1 - int Auto


NOTE - Running the above command (rather then using Visual Studio, VSCode or Rider to create a new project) ensures the project is created correctly.

Rider currently does not create the *.Client project needed when wanting to use the WebAssembly modes.

Project structure

A solution is created, with 2 projects;

The solution with the two projects structure is shown below;

Fig.4 - Project folder and file structure

Inside the Components and Pages folders are what are known as 'Razor components'.

Some notes about Razor components;

Outside of the Razor Components, inside the wwwroot folder, bootstrap css is included, as well as an entry point to the app in the form of Program.cs.

Code

Here is the source code of the counter page;

Fig.5 - The source of the counter page

Notice the rendermode on the second line (set to InteractiveAuto).

Here is a rundown of the types that can be used;

Client side rendering

InteractiveWebAssembly (Interactive WebAssembly)

In this mode, the logic code gets downloaded and is rendered purely in the browser via WebAssembly.

The browser downloads the .NET library and the app user code, and then runs them interactively.

This mode can be thought of as an alternative to tradional client-side JavaScript, where instead of JS, the browser is running C# code.

Server side rendering

With server rendering, the server is doing the heavy lifting, and the WebAssembly is purely used for HTTP communications.

[No rendermode] (Static server-side rendering - SSR)

Rendered on the server, HTML is sent to the client.

There is no interactivity - this ends up working very much like a tradional ASP.NET website.

InteractiveServer (Interactive server-side rendering - interactive SSR)

Rendered on the server but with SignalR (WebSockets) pushing interactive changes to and from the server.

Client and server rendered

InteractiveAuto (Automatic)

An optimised mix of interactive server and interactive WebAssembly. It will initially render server side, and then the .NET and app user code are downloaded to the browser. For interactivity the client side WebAssembly mode will be used if available (and if they can be downloaded quickly) - else it will use the server side model.

In truth, the automatic mode is a little more complicated then the simplification above. If you want to learn more about it, read 'Automatic (Auto) rendering' on https://learn.microsoft.com/en-us/aspnet/core/blazor/components/render-modes?view=aspnetcore-8.0

All the above rendering modes are set at a component level.

Running the project

Run the project in your IDE (make sure it is starting the main project and not the *.Client one).

Fig.6 - The 'Hello, world' homepage

Viewing the counter page

Clicking the 'Counter' link on the left hand side navigation will take you to the counter page.

Fig.7 - The counter page

Debugging (client side)

Press CMD + Shift + D in the browser window, to open the dev tools. If you get an error, follow the instructions on the page that opens (usually gives a terminal command to run, to open a new browser window with the correct permissions).

In the dev tools, open the Counter.razor file under files:/// and put a breakpoint on the currentCount++ line.

Click the 'Click me' button and your breakpoint should hit.

Fig.8 - Debugging the 'Click me' button on the counter page, client side

Changing to InteractiveWebAssembly rendermode

Stop running, go to Counter.razor in the IDE and change the 2nd line to the following;

Fig.9 - Updating rendermode to InteractiveWebAssembly

Debugging (client side)

Run the project as before and get the developer tools up. Setting a breakpoint and clicking the button should result in identical behaviour to before, as its still running client side WebAssembly.

Changing to InteractiveServer rendermode

Again, stop running, go to Counter.razor again in the IDE and change the 2nd line to the following;

Fig.10 - Updating rendermode to InteractiveServer

Debugging (server side)

Start debugging in the IDE. Add a breakpoint inside the IncrementCount method in your IDE, as below;

Fig.11 - Setting a breakpoint on the IncrementCount method

Click the 'Click me' button and your breakpoint should hit.

Fig.12 - Debugging the 'Click me' button on the counter page

Changing to static server side rendermode

Go to counter.razor for a final time and remove the 2nd line. It should look like the below;

Fig.13 - Removing the rendermode line

Debugging (or lack there of)

Add a breakpoint inside the IncrementCount method in your IDE and/or in the dev tools in the browser. Either way, it won't matter, as no breakpoint will be hit.

You will also notice that pressing the button does nothing. This is expected behaviour, as we have now disabled all interactivity.

JS interop

Decided you want to use a bit of JavaScript? Good news - Blazor allows calling JS from your C# code, and vice versa.

Calling JS from C#

Update the code to be like the below;

Fig.14 - Calling JS from the C# code

Run in the browser and see the following in the console when pressing 'Click me'.

Fig.15 - JS method writing to the console

Interestingly - if you switch to InteractiveServer, it ends up in a hybrid state where JS will still run in the browser, but you can see methods are firing server side.

Observe on the screenshot JS logging to the console, while a breakpoint hits in the IDE;

Fig.16 - JS method writing to the console, while a breakpoint is hit on the server

Calling C# from JS

Inversely, the C# code can also call JS. This can happen in either client or server modes.

Update the code to be like the following;

Fig.17 - Code where C# is called after 5 seconds

Set a breakpoint, debug this, wait 5 seconds when you get to the page, and you should see the breakpoint get hit.

Fig.18 - The C# method being triggered after 5 seconds

Mobile and desktop apps

Blazor can be used to make 'native' apps for mobile and desktop. The Blazor code runs in a WebView, similar to how Ionic / Capacitor / Cordova / PhoneGap work.

Alternatively, Blazor websites can be installed as PWAs.

Cons

References