This tutorial series is also available as an online video course. You can watch it on YouTube or enroll on Udemy. Or you just keep on reading. Enjoy! :)
Introduction
Blazor WebAssembly is turning the web development world upside down.
With Blazor you can keep coding every part of your web application - meaning front end and back end - with the programming language and the framework you love - C# and .NET.
No need for JavaScript anymore and you can even use the same classes and methods you write for the server as well as for the client.
In this tutorial series, we will dive right into the code by first having a look at the standard example project of Blazor WebAssembly and then we already build the main project of this tutorial series, which is a classic online browser game, where users can create an army of fighters and send them into battle against other users.
Together with some customization options and climbing the leaderboard, this application will teach you how to use Blazor WebAssembly with Razor components in a playful way.
We will have a look at data binding and event handling, communication between components, forms with their built-in components and validation options, how to use views only authorized users can see, how to make calls to a web service, and much more.
Additionally, you will learn how to build the back end of the browser game with a Web API and Entity Framework to store all the data in a SQLite database.
By the end of this tutorial series, you will have what it takes to call yourself a full stack Blazor web developer.
With your new skills, you are ready to conquer any upcoming .NET web development project you want to build yourself or any project that is requested by a recruiter.
Are you ready? Let's start!
Tools
The only tools you need in the beginning are the .NET SDK and Visual Studio.
Depending on when you’re watching this course, you can choose to download the .NET Core 3.1 SDK, the preview or release candidate of .NET 5 - which will combine .NET Core with the older .NET framework, or .NET 5 has already been released, then you’re safe to choose this SDK.
This course will use .NET Core 3.1 because, by the time of writing these lines, this version has been the latest stable release. So to be absolutely safe, please choose this version as well - but there shouldn’t be many differences between .NET Core and .NET 5 if any.
Regarding Visual Studio, you can use the Community Edition of Visual Studio 2019. It’s totally free and provides all the functions we need. If you decide to use a preview version of a .NET SDK, you probably also need a preview of Visual Studio. Just keep that in mind. Otherwise, the latest released version of Visual Studio is perfect.
If you already want to get the necessary tools for the back end, you can download and install Postman, which we will use to test our web service calls later on. Sometimes it’s just nice to test a call to the Web API before you build the corresponding front end.
Additionally, you could already download the DB Browser for SQLite, which enables you to, well, browse through a SQLite database.
But, again, these tools are used later in this tutorial series.
For now, please download and install the .NET SDK of your choice and Visual Studio 2019 Community Edition.
Git Repository on GitHub
One last thing before we start with creating our Blazor WebAssembly project.
You can get the complete code you’ll see during this tutorial series on GitHub.
Here’s the link to the repository: https://github.com/patrickgod/blazorbattles
As you can see, the commits in this repository match the structure of this tutorial series.
So if you’re struggling with your code, please have a look at this repository. I hope it helps to find a solution.
Or just grab the code and build your own browser game with it. Whatever you like.
Anyways, if you still have any problems with the code, you can also reach out to me, of course.
Now let’s create the project.
Jumpstart
Create an ASP.NET Core Hosted Blazor WebAssembly Project
Alright, when you start Visual Studio, choose Create a new project first.
From the templates, we choose a Blazor App. If you can’t find it, just use the search bar on top and look for blazor for instance.
Let’s give this thing a suitable name like BlazorBattles for instance and click Create.
Now it’s getting interesting. We have a bunch of options available here.
First the .NET SDK version. I’d like to use .NET Core 3.1.
Then we can choose between a Blazor Server App and a Blazor WebAssembly App. Well, according to the title of this tutorial series, we should choose Blazor WebAssembly.
If you don’t know the difference, just real quick: A Blazor Server App runs completely on the server. There are no actual web service calls like you might be used to with a typical web application.
The description says that user interactions are handled over a SignalR connection, meaning over web sockets. So the user doesn’t really download the client app to the browser, which is great.
But the big disadvantage is that the server has to handle everything. So if your app has a lot of users, the server has to manage all the interactions of every user, all current states, and so on.
With a Blazor WebAssembly application, the server has a lot less to do, because the client handles the interactions which might be faster for the user - speaking of user experience and offline functionality, for instance - and which is the typical structure of a modern web application.
Maybe you have already built a web application with a .NET back end and a JavaScript framework like Angular, React, or VueJS for the front end. These frameworks handle user interactions as well and make web service calls to the back end.
With Blazor WebAssembly it is the same thing, but you don’t have to write JavaScript.
You can keep writing C# code and even use the same classes for the client and the server. I just love that fact.
So, long story short, we choose the Blazor WebAssembly App.
We don’t need authentication for now. We will implement authentication later by ourselves, which is a great way to learn.
Configure for HTTPS is fine and then comes an important checkbox, we select ASP.NET Core hosted.
With that selection, we already get a server project and a shared project additionally to our client project.
We’ll have a look at these projects in the next chapter. For now, it’s just important to know that this checkbox provides a solution where we can already make use of a Web API. So, a full-stack web application in one solution with C# and .NET only.
That’s it.
At the bottom right you can double-check the used .NET (Core) version and then click Create.
And there’s our new solution. Great!
Let’s open the Solution Explorer on the right and then see what has been created for us.
Solution Overview
So, in the Solution Explorer of Visual Studio, you see three projects, Client, Server, and Shared.
The Client project represents the front end. Here’s where all the Blazor WebAssembly magic will happen.
The Server project will be the home of the Web API and Entity Framework.
And the Shared project will be used to share classes between the client and server projects. This means, building a model once and using it for both the client and the server. You can already see the WeatherForecast model here for instance, but we’ll talk about the example application in the next chapter.
Let’s have a look at the client project first.
The Program.cs
file with the Main()
method is the starting point. We will mainly use this method to register new services we write by ourselves or services that will be added by new packages.
namespace BlazorBattles.Client
{
public class Program
{
public static async Task Main(string[] args)
{
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("app");
builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
await builder.Build().RunAsync();
}
}
}
You can already see that something is happening here with the HttpClient
class, and above that line, a root component is added, which would be the App
component we can find in the App.razor
file.
<Router AppAssembly="@typeof(Program).Assembly">
<Found Context="routeData">
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
</Found>
<NotFound>
<LayoutView Layout="@typeof(MainLayout)">
<p>Sorry, there's nothing at this address.</p>
</LayoutView>
</NotFound>
</Router>
A component in Blazor is a razor file. That's why it's also called a Razor component.
So the App
component is the root component where you actually see the use of further components like the Router
, the Found
and NotFound
component, and the RouteView
for instance.
The Router
component that wraps the other ones, in this case, decides if a route that has been entered in the address bar of the browser is correct or not.
If it is correct, the Found
component will be used to display the content of the entered route, if not, the NotFound
component will be used. Both components, in turn, use further components, a RouteView
and a LayoutView
.
And these two make use of the MainLayout
. But let’s stick to the LayoutView
first. It uses the MainLayout
, but the content can already be seen here. It’s a simple text wrapped by a standard HTML paragraph tag.
<LayoutView Layout="@typeof(MainLayout)">
<p>Sorry, there's nothing at this address.</p>
</LayoutView>
The RouteView
though uses the MainLayout
but with the content of the actual page, the user wants to see by entering a particular route. And this page can be found in the Pages
folder.
We’ll get to that in a second.
Let’s have a quick look at the _Imports.razor
file. It’s quite simple, you will find global using directives here. That’s it. If you don’t want to add a reference in a single component, add it here instead.
@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 Microsoft.AspNetCore.Components.WebAssembly.Http
@using Microsoft.JSInterop
@using BlazorBattles.Client
@using BlazorBattles.Client.Shared
Okay, next, let’s have a look at the MainLayout
in the Shared
folder.
@inherits LayoutComponentBase
<div class="sidebar">
<NavMenu />
</div>
<div class="main">
<div class="top-row px-4">
<a href="http://blazor.net" target="_blank" class="ml-md-auto">About</a>
</div>
<div class="content px-4">
@Body
</div>
</div>
Looks like standard HTML at first glance, right?
Well, the only difference is that it inherits from the LayoutComponentBase
class. And when you look at this class, you can see that it has a property called Body
of type RenderFragment
which is used in the MainLayout
with @Body
. And that’s where the pages will be rendered.
namespace Microsoft.AspNetCore.Components
{
//
// Summary:
// Optional base class for components that represent a layout. Alternatively, components
// may implement Microsoft.AspNetCore.Components.IComponent directly and declare
// their own parameter named Microsoft.AspNetCore.Components.LayoutComponentBase.Body.
public abstract class LayoutComponentBase : ComponentBase
{
protected LayoutComponentBase();
//
// Summary:
// Gets the content to be rendered inside the layout.
[Parameter]
public RenderFragment Body { get; set; }
}
}
Additionally, you can see another used component, the NavMenu
, but we’ll get to that soon.
Do you already see how these things work together?
A component can be used by adding a tag with its exact name. And any page will be rendered in the @Body
part of a Layout.
You could build a custom layout if you like and use that one instead of the MainLayout
in the App.razor
component. Totally up to you.
Anyways, let’s have a look at the pages, for instance, the Index.razor
.
@page "/"
<h1>Hello, world!</h1>
Welcome to your new app.
<SurveyPrompt Title="How is Blazor working for you?" />
The crucial part is the @page
directive with a string that already is the route for that page. You don’t have to specify that route anywhere else. Just create a component, add that @page
directive, and you’re done.
@page "/"
It’s the same with the FetchData
and the Counter
pages.
Before we have a deeper look at the code, a quick word about the other projects.
The server project first consists of a Program.cs
and a Startup.cs
file.
In the Startup
class we find the ConfigureServices()
and the Configure()
method.
The ConfigureServices()
configures the app’s services, so a reusable component that provides app functionality.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddRazorPages();
}
We will register services in the future in this method, so they can be consumed in our web service via dependency injection for instance, similar to the Program.cs
of the client project.
Please don’t mind all these buzzwords right now…
The Configure()
method creates the server app’s request processing pipeline, meaning the method is used to specify how the app responds to HTTP requests.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseWebAssemblyDebugging();
}
else
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseBlazorFrameworkFiles();
app.UseStaticFiles();
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
endpoints.MapControllers();
endpoints.MapFallbackToFile("index.html");
});
}
As you can see we’re using HttpRedirection
, Routing
, and so on. With all these Use...()
extension methods, we’re adding middleware components to the request pipeline. For instance UseHttpRedirection()
adds middleware for redirecting HTTP requests to HTTPS.
Now the Startup
class is specified when the app’s host is built. You see that in the Program
class in the CreateHostBuilder()
method. Here the Startup
class is specified by calling the UseStartup()
method.
namespace BlazorBattles.Server
{
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
}
Regarding the appsettings.json
files we only need to know that we can add and modify some configurations here.
More interesting and often used throughout the back end part of this course is the Controllers
folder.
The first controller you see here is the generated WeatherForecast
demo controller. We’ll get to the details of controllers later. For now, it’s only important to know that we can already call the Get()
method here which is actually done by the FetchData
component.
[HttpGet]
public IEnumerable<WeatherForecast> Get()
{
var rng = new Random();
return Enumerable.Range(1, 7).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = rng.Next(-20, 55),
Summary = Summaries[rng.Next(Summaries.Length)]
})
.ToArray();
}
I’d say, we do that next.
Example Project Explained
Let’s run the application first and then talk about some files we haven’t talked about yet.
The great thing about Visual Studio and .NET Core hosted Blazor WebAssembly applications is that you can simply start this whole package with that little play button on top. Let’s do exactly that.
The actual starting project is the Server project and Visual Studio will fire up IIS Express with a browser of your choice to start the app.
When you have a close look, you see the text “Loading…”. Where does that come from, you might ask?
Well, the client project has got this folder called wwwroot
which is the root of the actual website, and here next to some cascading style sheets and a favicon you can find the actual index.html
. And here you find the loading text.
<app>Loading...</app>
Any resources you want to add later, like icons, images, other stylesheets, and so on, have to be added to this wwwroot
folder. We will use this place later.
The crazy thing now is, that you can actually debug the client and the server at the same time.
You see, that the counter page increases a counter.
Let’s have a look at the code.
@page "/counter"
<h1>Counter</h1>
<p>Current count: @currentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
@code {
private int currentCount = 0;
private void IncrementCount()
{
currentCount++;
}
}
It seems like there’s not much to see, but actually, there’s already happening a lot.
You already know the @page
directive.
Then you can use the @currentCount
variable.
<p>Current count: @currentCount</p>
This integer variable can also be found in the @code
block at the bottom.
private int currentCount = 0;
So in this @code
block, you’re free to write C# code and use that code above within your HTML code. With the @currentCount
variable you already see how data binding is done in Blazor WebAssembly.
And in the button, you can already see event handling.
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
The onclick
event, marked with an @
calls the IncrementCount()
method. Simple as that.
By the way, if you don’t like having your C# code together with HTML, you can create a code-behind file for your C# code instead.
Now let’s set a breakpoint inside the IncreaseCount()
method.
We hit the Click me button and hit the breakpoint. Crazy.
Now, what’s up with the service? Let’s have a look at the FetchData
page.
This page actually makes a call to the Web API of the server project. We can see that in the network tab of the console.
Looking at the code, we see a lot of new stuff.
@page "/fetchdata"
@using BlazorBattles.Shared
@inject HttpClient Http
<h1>Weather forecast</h1>
<p>This component demonstrates fetching data from the server.</p>
@if (forecasts == null)
{
<p><em>Loading...</em></p>
}
else
{
<table class="table">
<thead>
<tr>
<th>Date</th>
<th>Temp. (C)</th>
<th>Temp. (F)</th>
<th>Summary</th>
</tr>
</thead>
<tbody>
@foreach (var forecast in forecasts)
{
<tr>
<td>@forecast.Date.ToShortDateString()</td>
<td>@forecast.TemperatureC</td>
<td>@forecast.TemperatureF</td>
<td>@forecast.Summary</td>
</tr>
}
</tbody>
</table>
}
@code {
private WeatherForecast[] forecasts;
protected override async Task OnInitializedAsync()
{
forecasts = await Http.GetFromJsonAsync<WeatherForecast[]>("WeatherForecast");
}
}
First the using directive. We’re referencing the Shared
project here, because we’re using the WeatherForecast
class.
Then we inject the HttpClient which enables us to make web service calls.
@using BlazorBattles.Shared
@inject HttpClient Http
Inside the table, we see a foreach
loop that uses the forecasts received from the service.
<tbody>
@foreach (var forecast in forecasts)
{
<tr>
<td>@forecast.Date.ToShortDateString()</td>
<td>@forecast.TemperatureC</td>
<td>@forecast.TemperatureF</td>
<td>@forecast.Summary</td>
</tr>
}
</tbody>
We’ll do all this later by ourselves, so please don’t mind my pacing for now if it’s a bit too fast.
In the @code
block we see a new method called OnInitializedAsync()
. This thing is part of the component lifecycle and is called, as the name may imply, on the initialization of the component.
protected override async Task OnInitializedAsync()
{
forecasts = await Http.GetFromJsonAsync<WeatherForecast[]>("WeatherForecast");
}
And in this method we’re using the injected HttpClient
to make a Get()
call to the WeatherForecast
route which would be the WeatherForecastController
of the server project, which is, by the way, also using the WeatherForecast
class of the Shared
project.
Let’s move to that controller and set a breakpoint in the Get()
method and then run the app again.
When we open the FetchData
page, the breakpoint in the service is hit. Fantastic!
Now, although being able to debug client and server at the same time is great, I like to run the application in another way.
Because with the way I just showed you, you have to stop and run the project by yourself every single time you made some changes to your code. This slows down development.
So instead, I like to use the Package Manager Console with the dotnet watch run
command, which rebuilds the project when any code change has been recognized.
There's only one problem: So far this only works for the server project. If we make any changes to the client project, they won’t be recognized. But, of course, we can fix that.
In the project file of the server, we add a new ItemGroup.
<ItemGroup>
<Watch Include="..\Client\**\*.razor" />
</ItemGroup>
With that little change, the application notices any change happening in any razor
file of the client project - additionally to any change in the server project.
Now let’s open the Package Manager Console and then first move to the directory of the server.
Now enter dotnet watch run
. When the app is running, you already see the URL we have to enter in Chrome.
This URL can also be found in the launchSettings.json
files of your projects.
Now the breakpoints won’t be hit, but let’s increase the counter by 2 for instance.
private void IncrementCount()
{
currentCount += 2;
}
The change will be recognized, the app is rebuilt and we just have to reload the page to see the change.
The same for the server. Let’s return seven forecasts instead of five and reload the page.
[HttpGet]
public IEnumerable<WeatherForecast> Get()
{
var rng = new Random();
return Enumerable.Range(1, 7).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = rng.Next(-20, 55),
Summary = Summaries[rng.Next(Summaries.Length)]
})
.ToArray();
}
Now we see seven forecasts.
You see, this makes developing your Blazor WebAssembly app a lot faster.
One thing we haven’t talked about yet is the NavMenu
. But by now, you may already know how this component works.
You see some data binding and event handling with the ToggleNavMenu()
function and the NavMenuCssClass
variable.
<div class="top-row pl-4 navbar navbar-dark">
<a class="navbar-brand" href="">BlazorBattles</a>
<button class="navbar-toggler" @onclick="ToggleNavMenu">
<span class="navbar-toggler-icon"></span>
</button>
</div>
<div class="@NavMenuCssClass" @onclick="ToggleNavMenu">
<ul class="nav flex-column">
<li class="nav-item px-3">
<NavLink class="nav-link" href="" Match="NavLinkMatch.All">
<span class="oi oi-home" aria-hidden="true"></span> Home
</NavLink>
</li>
<li class="nav-item px-3">
<NavLink class="nav-link" href="counter">
<span class="oi oi-plus" aria-hidden="true"></span> Counter
</NavLink>
</li>
<li class="nav-item px-3">
<NavLink class="nav-link" href="fetchdata">
<span class="oi oi-list-rich" aria-hidden="true"></span> Fetch data
</NavLink>
</li>
</ul>
</div>
@code {
private bool collapseNavMenu = true;
private string NavMenuCssClass => collapseNavMenu ? "collapse" : null;
private void ToggleNavMenu()
{
collapseNavMenu = !collapseNavMenu;
}
}
New though is the NavLink
component. This is a built-in component for navigation links.
<NavLink class="nav-link" href="counter">
<span class="oi oi-plus" aria-hidden="true"></span> Counter
</NavLink>
The description already says it all: It’s a component that renders an anchor tag, automatically toggling its active
class based on whether its href
matches the current URI. And the href
would be the string that is used with the @page
directives of your pages.
One last thing I want to show you in this example application is the first way of communication between components.
When we switch to the Index.razor
again, you see the use of the SurveyPrompt
component with a Title
.
<SurveyPrompt Title="How is Blazor working for you?" />
This Title
is actually a parameter. So the parent component Index
can tell the child component SurveyPrompt
anything with the help of parameters.
Looking at the SurveyPrompt
component, you see the Title
property in the @code
block, marked by a [Parameter]
attribute.
@code {
// Demonstrates how a parent component can supply parameters
[Parameter]
public string Title { get; set; }
}
And then this Title
- provided by the parent - is used in the HTML above.
<strong>@Title</strong>
Alright, that should be it for the example application. Already a lot of input I guess.
Let’s make a new Git repository out of this and then start building our browser game with Blazor.
Initialize Git Repository
To initialize a Git repository, on the bottom right of Visual Studio we click on Add to Source Control and choose Git.
And that’s it actually. Now we’ve got a local repository where we can commit our changes and have a history of them.
We could also publish this repository to any remote one or use GitHub or Azure DevOps, for instance. This could also be a private repository. So if you want to save your code to the cloud feel free to do so.
As mentioned earlier, you will find the code of this tutorial series on GitHub as well. So if you have any trouble or just want to play around with the project, feel free to access this project on GitHub, clone the repo, fork it, star it, and so on.
Alright, that’s it for this part of the Blazor WebAssembly Full Stack Bootcamp. Let’s make a game next.
That's it for the first part of this tutorial series. I hope it already was useful to you. To get notified for the next part, simply follow me here on dev.to or subscribe to my newsletter. You'll be the first to know.
See you next time!
Take care.
Next up: Your first Razor component, communication between components with parameters, event callbacks and services, create a new page, and more!
Thumbnail: vector illustration/Shutterstock
But wait, there’s more!
- Watch the Blazor WebAssembly Full Stack Bootcamp online course
- Subscribe to my newsletter for exclusive content
- Get the 5 Software Developer’s Career Hacks for free.
- Let’s connect on Twitter, YouTube, LinkedIn or here on dev.to.
Top comments (1)
Top de +.