DEV Community

axurcio
axurcio

Posted on • Originally published at insight-services-apac.github.io on

Getting started with Blazor

Blazor is a Single Page Application development framework. The name Blazor comes from the words Browser and Razor.

Blazor either runs server-side or client-side. With the server-side model it executes on a server and sends the rendered HTML and CSS to the browser. In the Blazor Server Model UI events are sent back to the server using SignalR then the UI changes are sent to the client and merged into the DOM. Blazor client-side model runs in the browser by utilising WebAssembly. It does not require any plugin installed on the browser. Since WebAssembly is a web standard, it is supported on all major browsers and devices. Code running in the browser executes in the same security sandbox as JavaScript frameworks.

Why use Blazor

The first question you would ask straight away when you hear about Blazor is why Blazor? And why not use another JavaScript framework like React or Angular. This is not an easy question to answer. JavaScript frameworks have been there for a while and gained a lot of popularity and success. I’m not trying to convince you with Blazor over other UI frameworks here, but I’m going to list the features and capabilities that attract most people to Blazor and might be good reasons for you too to start using Blazor.

.NET Experience

This might be the primary selling point of Blazor. If you are a .NET developer with no or little JavaScript experience Blazor allow you to start developing web applications without having to learn a new technology. You can use libraries or frameworks that you already use with your .NET projects as long as they are compatible with .NET Standard.

WebAssembly

WebAssembly (abbreviated Wasm) is designed as a portable compilation target for programming languages, enabling deployment on the web for client and server applications. In November 2017, Mozilla declared support in all major browsers after WebAssembly was enabled by default in Edge 16. The support includes mobile web browsers for iOS and Android.

JavaScript is a high-level language, flexible and expressive enough to write web applications and has a huge ecosystem that provides powerful frameworks, libraries, and other tools, whereas WebAssembly is a low-level assembly-like language with a compact binary format that runs in the browser with near-native performance.

Blazor supports JavaScript interoperability. Using Blazor does not mean “No JavaScript”. You can still call JavaScript functions from .NET apps and vice versa.

Code Reusability

With Blazor you are using .NET throughout your different application layers. Developers in your team can easily work with backend or frontend. This means that you can reuse code between your backend and frontend, use your common libraries across application layers and share API models. Think about utilities like string formatters, DateTime helpers, cryptography and a lot more. You can now have one library that you can use across all different projects - backend or frontend.

Server-side Rendering

One of the primary requirements that you would frequently be asked for when developing SEO friendly websites is server-side rendering. Server-side rendering is important when you build applications intended to be crawled by search engines as it allows bots to crawl your website contents without having to execute JavaScript code. By using Blazor, server-side rendering is included as standard.

Blazor WebAssembly vs. Blazor Server

Blazor WebAssembly

In Blazor WebAssembly (or the client-side hosting model) the application runs directly in the browser on WebAssembly. So, the compiled application code itself, dependencies and the .NET runtime are downloaded to the browser. This means that the application will need some time to download and load the application and dependencies assemblies at start-up. However, this means the app remains functional if the server goes offline.

Blazor WebAssembly Hosting Model

Reference: ASP.NET Core Blazor hosting models

Blazor Server

In Blazor Server (or the server hosting model) the application is executed on the server from within an ASP.NET Core application. Between the client and the server, a SignalR connection is established. When an event occurs on the client such as a button click the information about the event is sent to the server over the SignalR connection. The server handles the event and the generated HTML. The entire HTML is not sent back again to the client, it’s only the diff that is sent to the client over the established SignalR connection. The browser then updates the UI.

Because code run on the server, the initial load is much faster than Blazor WebAssembly. The application can take advantage of server capabilities such as .NET Core APIs which allows utilizing the existing tooling such as debugging. However, the main concern with using this model is the high latency as every user interaction will require a network hop and this means also that there is no offline support.

Blazor Server Hosting Model

Reference: ASP.NET Core Blazor hosting models

Create your first app

In this article, we’ll go through the steps of how to create and deploy a Blazor WebAssembly app. There are two types of Blazor WebAssembly apps, stand-alone and hosted models.

Standalone is suitable when you intend to deploy your app without an ASP .NET Core app to serve it, for example hosting the app on IIS server or Azure App Service (windows).

Hosted the app uses an ASP.NET Core server to host the client App which gives you the flexibility to host it in Azure App Service (Windows or Linux) and allow you to share code between client and server apps.

In this article, we’ll be using hosted Blazor WebAssembly.

Create the project

Visual Studio Code

For the command line run the following:

dotnet new blazorwasm --hosted -o FirstBlazorApp

Visual Studio

From Visual Studio Choose Blazor WebAssembly App template and make sure ASP.NET Core hosted option is selected.

VS Create Blazor App

Now open the project in your favourite editor and you will get the following folder structure:

Blazor Hosted Folder Structure

You will have three projects created in your solutions.

  • Shared a class library project referenced by both Client and Server and the purpose of this project is to include all the code shared between the client and server such as API models.
  • Client the Blazor WASM project. It has all the code required to run your Blazor client App.
  • Server an ASP.NET Core project. You can create your APIs in this project which will run in the same environment as your client app. This app also acts as a host server to run your client app. This is enabled in the Program.cs file by a simple line: app.UseBlazorFrameworkFiles();

Run the project

To run the project:

  • Go to Client folder (if you want to run the standalone client without the server) or Server folder (if you want to run both server and client).
  • Run this command to start the app dotnet run
  • From the browser navigate to http://localhost:5173/

Walkthrough

Under Client folder we have the following:

  • _imports.razor contains all the namespaces imported and used by the project.
  • App.razor is the main component of the app. It has the Router component which loads all the pages inside the RouteView component. If the URL was not found the app displays the template in NotFound component.
<Router AppAssembly="@typeof(App).Assembly">
  <Found Context="routeData">
    <RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
    <FocusOnNavigate RouteData="@routeData" Selector="h1" />
  </Found>
  <NotFound>
    <PageTitle>Not found</PageTitle>
    <LayoutView Layout="@typeof(MainLayout)">
      <p role="alert">Sorry, there's nothing at this address.</p>
    </LayoutView>
  </NotFound>
</Router>

Enter fullscreen mode Exit fullscreen mode
  • wwwroot has the client-side assets and styles. In Blazor WebAssembly index.html is the main entry point to the application. You can add any CSS or JavaScript references to this page. You can also redesign how the loading page should look during the initial load.
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta
      name="viewport"
      content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"
    />
    <title>FirstBlazorApp</title>
    <base href="/" />
    <link href="css/bootstrap/bootstrap.min.css" rel="stylesheet" />
    <link href="css/app.css" rel="stylesheet" />
    <link href="FirstBlazorApp.Client.styles.css" rel="stylesheet" />
  </head>

  <body>
    <div id="app">Loading...</div>

    <div id="blazor-error-ui">
      An unhandled error has occurred.
      <a href="" class="reload">Reload</a>
      <a class="dismiss">🗙</a>
    </div>
    <script src="_framework/blazor.webassembly.js"></script>
  </body>
</html>

Enter fullscreen mode Exit fullscreen mode
  • Shared Under Shared folder, you will find the MainLayout.razor and all other shared components. MainLayout has been set as the DefaultLayout in the App.razor.
  • Pages Under pages, you will find the components which will be rendered inside the MainLayout. In the page component, there are three main pieces you need to understand:
    • @page directive which defines the route Url for the current page
    • PageTitle overrides the application page title.
    • @code block allows you to write your code in the same file with the razor template.
@page "/counter"

<PageTitle>Counter</PageTitle>

<h1>Counter</h1>

<p role="status">Current count: @currentCount</p>

<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>

@code { 
    private int currentCount = 0; 

    private void IncrementCount() 
    {
        currentCount++; 
    } 
}

Enter fullscreen mode Exit fullscreen mode

Code-behind

As you can see in the previous example both the HTML markup and the C# code exist in the same file, which makes it easy and simple to build components in Blazor. However, as the business grows and requirements get bigger and more complex, splitting the code into a Code-behind file might be a good idea to keep the code clean and easy to maintain.

Splitting Blazor’s component code into a separate file is easy as all .razor files become classes when the project is compiled. For example, the code inside the Counter.razor file gets extracted into a class called Counter when compiled.

To move the code inside a component into a separate class there are two methods to achieve that:

Partial Class

To create a partial class for the Counter component, create a new class file and call it Counter.razor.cs then add the following code to it:

namespace FirstBlazorApp.Client.Pages;
public partial class CounterComponent
{
    private int currentCount = 0;

    private void IncrementCount()
    {
        currentCount++;
    }
}

Enter fullscreen mode Exit fullscreen mode

Now, you can remove the code block from Counter.razor and build the project and you’ll find that it builds successfully with no errors.

Inheriting ComponentBase class

Instead of creating a partial class for your components, you can create a class that inherits from ComponentBase class. You can then remove the code block from the .razor file and use the @inherits directive to inherit from the class you have just created.

Note that in this method you need to modify access modifiers from private to protected to be able to access them from the .razor file.

Because the class inherits from ComponentBase, another benefit you get is that you have access to the component’s lifecycle methods such as OnInitializedAsync().

You can find more details about component lifecycle in the following article: ASP.NET Core Razor component lifecycle

  • Counter.razor.cs
using Microsoft.AspNetCore.Components;
namespace FirstBlazorApp.Client.Pages;
public class CounterComponent : ComponentBase
{
    protected int currentCount = 0;

    protected void IncrementCount()
    {
        currentCount++;
    }

    protected override Task OnInitializedAsync()
    {
        return base.OnInitializedAsync();
    }
}

Enter fullscreen mode Exit fullscreen mode
  • Counter.razor
@page "/counter"
@inherits CounterComponent

<PageTitle>Counter</PageTitle>

<h1>Counter</h1>

<p role="status">Current count: @currentCount</p>

<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>

Enter fullscreen mode Exit fullscreen mode

Scoped styles

Blazor supports scoping CSS to Razor components, which can simplify CSS and avoid collisions with other components or libraries.

To enable CSS isolation is simple. Create a .razor.css file matching the name of the .razor component file in the same folder. For example, to create isolated CSS for the Counter component create a file called Counter.razor.css in the same folder as Counter.razor file.

The styles defined in Counter.razor.css are only applied to the rendered HTML of the Counter component.

For more details about Blazor CSS isolation see the following article:

ASP.NET Core Blazor CSS isolation

Writing unit tests

In unit tests, only the Razor component (Razor/C#) is involved. External dependencies, such as services and JS interop, must be mocked.

There’s no official Microsoft testing framework for Blazor, but Microsoft recommendsusing bUnit which provides a simple and easy way to unit test Razor components.

The good news is that bUnit works with common testing frameworks, such as MSTest, NUnit, and xUnit which makes bUnit tests feel like regular unit tests.

Let’s go through the steps of how to start writing unit tests with bUnit using xunit testing framework:

  1. Create a xunit test project by running: dotnet new xunit -o FirstBlazorAppTests
  2. Under FirstBlazorAppTests add reference to FirstBlazorApp project: dotnet add reference ../Client/FirstBlazorApp.Client.csproj
  3. Add the following NuGet packages: dotnet add package bunit.web dotnet add package bunit.xunit
  4. Now you can start writing unit tests as you would normally do with any .NET project

The following is an example of a test class for the Counter component:

using Bunit;
using Xunit;
using FirstBlazorApp.Client.Pages;

namespace FirstBlazorApp.Tests;

public class CounterComponentTests
{
    [Fact]
    public void CounterComponentTest()
    {
        // Arrange: Render the Counter component
        using var ctx = new TestContext();
        var cut = ctx.RenderComponent<Counter>();

        // Act: Find and click the <button> element
        cut.Find("button").Click();

        // Assert: Find the <p> element, then verify its content
        cut.Find("p").MarkupMatches(@"<p role=""status"">Current count: 1</p>");
    }
}

Enter fullscreen mode Exit fullscreen mode

For more details about bUnit see the official bUnit documentation:

Testing Blazor components

Deploy the Blazor app

In Blazor Server model a web server capable of hosting an ASP.NET Core app is required in order to run your Blazor app. However, in Blazor WebAssembly there are two deployment strategies:

  1. Standalone deployment The app is hosted on a static web server where .NET is not used to serve the app. Any static file server can host the Blazor app.
  2. Hosted deployment The app is served by an ASP.NET Core app that runs on a web server. As you can see in the previous example, we have two main Projects (Client and Server). The Client project produces the static files required to run the app whereas the Server project contains the ASP.NET Core app that acts as a small server to host the client app.

Deploy to Azure App Service

You can publish and deploy the Blazor app directly from VS Code which is a convenient and efficient way to deploy Blazor apps during the development process. However, for production, you should always consider using Continuous Deployment to deploy your apps.

Deploy from VS Code

Let’s go through the steps for how to deploy FirstBlazorApp that we have just created to Azure App Service using VS Code.

  1. Install Azure App Service extension and configure it: Azure App Service VS Code Extension.
  2. Publish the app. When you deploy Blazor WASM to Azure App Service windows you can use both strategies (Standalone or Hosted), however, if you wish to host the app on Azure App Service Linux you won’t be able to use the Standalone strategy.
    1. Standalone deployment (windows): Publish the Client app to generate the deployment package: dotnet publish Client -c Release -o ./publish
    2. Hosted deployment (Windows or Linux): dotnet publish Server -c Release -r linux-x64 -o ./publish
  3. Now right click on the publish folder that has been just created and select Deploy to Web App…

Deploy from Azure DevOps

Finally, we’ll go through the steps of how to configure Azure DevOps pipelines to build, test and deploy our FirstBlazorApp to Azure App Service. To do so:

  1. First, push your code to a new repository.
  2. From the Azure Portal create an Azure App Service and set the runtime stack to .NET 6.
  3. Go to Azure DevOps and create a new pipeline.
  4. Select your code repo and proceed to the Review step.
  5. Go to Project Settings > Service connections and create a new service connection then select Azure Resource Manager type then choose Service Principal (automatic) and set scope level to subscription.
  6. Click Variables and add the required variables
    • AzureServiceConnection The name of the Service connection created in the previous step.
    • ResourceGroup The name of the resource group where your app service is created.
    • WebAppName The name of the App Service to deploy the app to.
  7. Paste the following YAML then click Save and Run
trigger:
  - main

pool:
  vmImage: ubuntu-20.04

variables:
  buildConfiguration: "Release"
  dotNetVersion: "6.0.x"
  location: "australiaeast"
  dotNetFramework: "net6.0"
  webAppDir: "$(Build.SourcesDirectory)/Server"
  webAppTestDir: "$(Build.SourcesDirectory)/Tests"

steps:
  - task: UseDotNet@2
    displayName: Use .NET 6.0
    inputs:
      packageType: "sdk"
      version: "6.0.x"

  - script: dotnet build --runtime linux-x64 --configuration $(buildConfiguration)
    workingDirectory: "$(webAppDir)"
    displayName: "Building App..."

  - task: DotNetCoreCLI@2
    displayName: "Testing App..."
    inputs:
      command: test
      projects: "$(webAppTestDir)/*.csproj"
      arguments: "--configuration $(buildConfiguration)"
      publishTestResults: true

  - task: DotNetCoreCLI@2
    displayName: "Publishing App..."
    inputs:
      command: publish
      workingDirectory: "$(webAppDir)"
      publishWebProjects: false
      zipAfterPublish: True
      arguments: "--runtime linux-x64 --configuration $(BuildConfiguration) --output $(Build.ArtifactStagingDirectory)"

  - task: AzureRmWebAppDeployment@4
    displayName: "Azure App Service Deploy"
    inputs:
      azureSubscription: "$(AzureServiceConnection)"
      ResourceGroupName: "$(ResourceGroup)"
      appType: webAppLinux
      WebAppName: "$(WebAppName)"
      Package: "$(Build.ArtifactStagingDirectory)/**/*.zip"

Enter fullscreen mode Exit fullscreen mode

References

  1. ASP.NET Core Blazor.
  2. bUnit.
  3. WebAssembly.
  4. Single-page application.
  5. ASP.NET Razor.
  6. Host and deploy ASP.NET Core Blazor WebAssembly.

Top comments (0)