Blazor: .NET in the Browser, Part 1
In Part 1 of this two-part series, we introduce Blazor and demonstrate how to make a quick and simple client-side application.
Join the DZone community and get the full member experience.
Join For FreeIn this article and tutorial we are going to use Blazor to bring C# in the browser. We are going to talk about what you can do and the things you have to think about when creating a Blazor app.
WebAssembly is a great platform that promises to revolutionize development on the browser. Thanks toWebAssembly, JavaScript is no longer the only language that you can use client-side when developing for the web. It is an exciting platform, which we already talked about in Understand WebAssembly. In this article, we go one step further see how to use a platform that runs on WebAssembly: Blazor.
What is exactly Blazor?
Full-stack web development with C# and WebAssembly
Everything you can do with C#, you can do on Blazor. In addition to that, you can obviously interact with JavaScript code, just as you would expect to do a WebAssembly project.
Technically, Blazor runs on Mono, which is an open-source and cross-platform version of the .NET Framework developed by Xamarin, a company that is now part of Microsoft. So, it does not run on .NET Core. This is technical information that, generally, has little practical consequences, but it may be useful to know in some circumstances. Also, in older browsers, where WebAssembly is not present, Blazor relies on the asm.js sub-set of JavaScript.
The last thing to say in the introduction is that Blazor is still an experimental project, but it is already quite successful: with an official website, a good community and many resources.
Why Blazor?
Why would you want to use C# in place of JavaScript? The usual reason is because you might prefer one language over the other:
- Better support for what you care about.
- An old code base to support.
- A need for a unified code base.
In addition to that, JavaScript is a bit lacking when managing big and complex projects. More than one language was invented for this scope. Probably the most successful is TypeScript, which has the slogan: JavaScript that scales. C# can be an alternative solution, allowing you to use it for the complex parts of the application and leave JavaScript for the UI.
Setting Up Blazor
All you need to use Blazor is .NET Core 2.1.300 SDK or later. If you use Visual Studio, you can also install the Blazor Language Services extension to have Blazor templates available in the IDE. However, it is not required to use Visual Studio. In fact, in this article we are going to use Visual Studio Code.
You might wonder why we are using .NET Core, even though we just said that Blazor relies on Mono. Certainly, Blazor runs on Mono, as you can see inside the dist folder: mono.wasm
contains the Mono compiled to WebAssembly, mono.js
file contains glue code.
However, the app must also distributed to the user in some way. The default template uses ASP.NET Core and a web server to do it; just like your typical JavaScript app. In theory, you could pack it some other way, like an Electron app, but this is still not supported.
Once you have installed .NET Core you can install the Blazor templates from the command line, if you are not using Visual Studio.
# install the Blazor Templates
dotnet new -i Microsoft.AspNetCore.Blazor.Templates
# create a new Blazor App
dotnet new blazor -o BlazorApp
# run the app, as usual
cd BlazorApp
dotnet run
When creating the Blazor app you should go to the address indicated (usually localhost:5000 or localhost:5001 with TLS) to check that everything is working. If you see an image like the following one, everything is ok.
Common Errors
Instead, if you see a blank page, something is wrong. The most probable error is that you have installed an older version of .NET Core SDK. Remember that, even if you have the wrong version of .NET Core SDK, you can still successfully install the templates and create the app. The installation will succeed but the app will not work. So, check that you are using the correct version of the .NET Core SDK.
At the moment, if you go to the official .NET website, you get the right version by default for Windows and macOS, but you might still get an older version for some Linux distributios. For instance, for Ubuntu, .NET Core SDK 2.1.300 is still at the Release Candidate stage.
It's important to remember that the .NET Assemblies remain untouched, it is just the .NET Framework (i.e., Mono) that is compiled to WebAssembly. This means that any C# code should work the same way in Blazor as in any other .NET implementation.
In this article we are using Blazor version 0.5.1.
Anatomy of a Blazor App
Now that everything is properly setup we can create our first Blazor app. But, first, let's take a look at the default template and specifically at the Index.cshtml
file under Pages
. It is the same page that we have seen when we first launched the app to test whether it was working.
@page "/"
<h1>Hello, world!</h1>
Welcome to your new app.
<SurveyPrompt Title="How is Blazor working for you?" />
This is a short page but it contains a few interesting things. It is a traditional Razor page, that is well used in the C# world, but with a few Blazor additions. If you have never used Razor, you can still easily understand it since it is a typical templating language mixed with HTML.
Routing
The @page
directive is used to configure routing. This mean that this page will answer when the home root (/
) is requested. You can also use normal ASP.NET route parameters such as in the following example.
@page "/{name}"
This page will assign the value specified in the URL to the variable name
. Notice that, at the moment, optional elements are not supported. So, to support a route with an optional parameter instead you have to add to page directives, one with the optional parameter and one without.
// in normal ASP.NET you would write "/{name?}" for an optional parameter
@page "/"
@page "/{name}"
Elements
The second interesting thing of this page is the last line, the SurveyPrompt
element. It looks and behaves like a normal HTML element, but obviously is not. In fact it is what Blazor calls a component, which is defined in the SurveyPrompt.cshtml
page under the Shared
folder. In Blazor, any page defines a corresponding component with the same name.
<div class="alert alert-secondary mt-4" role="alert">
<span class="oi oi-pencil mr-2" aria-hidden="true"></span>
<strong>@Title</strong>
<span class="text-nowrap">
Please take our
<a target="_blank" class="font-weight-bold" href="https://go.microsoft.com/fwlink/?linkid=874928">
brief survey
</a>
</span>
and tell us what you think.
</div>
@functions {
[Parameter]
string Title { get; set; } // Demonstrates how a parent component can supply parameters
}
On line 3, we see the syntax to display the variable Title
inside the HTML UI. This is standard Razor syntax. The new Blazor-specific part is at the end, with the functions
directive. Here we can use standard C# to add functionality to the page. Notice that the Title
attribute that we used in the index page it is configured as a parameter in this functions section. This is done with the [Parameter]
annotation.
Navigation
Lastly, let's look at the standard template of a Blazor app, the page NavMenu.cshtml
.
<li class="nav-item px-3">
<NavLink class="nav-link" href="calendar">
<span class="oi oi-calendar" aria-hidden="true"></span> Dates
</NavLink>
</li>
This is the example of a menu item that is using the NavLink
component, which is basically a simple element that adds the CSS class active
when the element is selected. Let's do some cleanup and remove some stuff from the standard template that we are not going to use. We add this element and delete the ones already present. Then we delete corresponding pages and add a new page called Calendar.cshtml
. So, our pages should now look like this.
Creating Our App
In our app, we want a page that shows the same date in a few different calendars.
@page "/"
@page "/calendar"
We associate the page with both the default route and the route calendar. This is useful to see one thing: when launching the app you will see what happens when changing pages. When visiting one of the pages, the underlying data will remain the same. So, they are not two different pages, but the same instance of a page, but one that answers to two different addresses.
@page "/"
@page "/calendar"
<div class="container">
<div class="row">
<div class="col-sm input-row">
<h1>Date in Different Calendars</h1>
<p>Select a date</p>
<input type="date" bind="@date" placeholder="@DateTime.Now"/>
<button class="btn btn-primary" onclick="@ConvertDate">Convert Date</button>
</div>
</div>
<!-- the calendars -->
<div class="row calendar-row">
<div class="col-sm">
<div class="card">
<div class="card-body">
<h5 class="card-title">Julian Calendar</h5>
<p class="card-text"><p>@calendarDate.JulianDate</p></p>
</div>
</div>
</div>
[..]
</div>
[..]
</div>
The UI is divided in two parts: the first section contains the input, while the second displayS the same data provided by the user in different calendars. The input, on line 9, is bOUnd to the variable date
, while, as a placeholder, we use the field DateTime.Now
. On line 11, we bind a function to the click action, using standard HTML syntax. This works basically as if it were a JavaScript function.
The parts where we display calendars is only partially shown here, given that is quite repetitive. The final version present in the repository contains three rows of three calendars each, for a total of nine calendars. Each calendar looks like the one shown here: a card (a card is a Bootstrap element) displaying the name of the calendar, and the date itself. The date is a field of a custom class we created.
@functions {
string date = DateTime.Today.ToString("yyyy-MM-dd");
CalendarDate calendarDate = new CalendarDate();
void ConvertDate()
{
if (!string.IsNullOrWhiteSpace(date))
{
calendarDate.StringDate = date;
}
}
}
The part containing the functions
directive does not have any surprises: we setup the variables and the function that we need in the UI. What happens is simply that we select a date with the input element, then when we click the button we convert the input date in the dates shown in the calendars. This is dynamic behavior implemented in C#, just what we needed. But what about the CalendarDate
class?
The C# CalendarDate Class
This is a class that we can create: first, we need to add a new root-level C# file, called CalendarDate.cs
.
using System;
using System.Globalization;
using System.Text;
public class CalendarDate
{
private String stringDate = DateTime.Now.ToString();
public String StringDate {
get {
return stringDate;
}
set {
stringDate = value;
Date = DateTime.Parse(stringDate);
}
}
public DateTime Date { get; set; } = DateTime.Now;
private string GetDateForCalendar(Calendar cal)
{
if(Date > cal.MinSupportedDateTime && Date < cal.MaxSupportedDateTime)
return $"{cal.GetDayOfMonth(Date):d2}/{cal.GetMonth(Date):d2}/{cal.GetYear(Date):d4}";
else
return "Invalid Date";
}
public string JulianDate
{
get {
Calendar cal = new JulianCalendar();
return GetDateForCalendar(cal);
}
}
[..]
}
This is simple C# code, with nothing special in it. When we assign a value to the StringDate
field, we also automatically assign the corresponding value to the Date
field. Basically, the input is a String
, but we need to convert it to a DateTime
variable. On line 17, we assign a default value to our Date
field, so that initially the calendars show a valid date.
The GetDateForCalendar
ensures that, if the date selected is valid for that calendar (line 21), we convert it to a String holding the representation of that date in the specified calendar (line 22). If the date is not valid, we return a string to denote that. This is necessary because not all calendars can deal with all dates, sometimes it is a problem of the calendar itself, sometimes the issue is with .NET implementation or the fact that we do not deal with the concept of eras.
After we added a bit of CSS in the file www-root/css/site.css
(not shown), we can launch the program with dotnet run
. The final result is quite neat.
Look at that, dynamic behavior without a hint of JavaScript.
So, can we ditch JavaScript altogether? Well, not quite. You probably going to want to keep using JavaScript to manipulate the DOM. Though before seeing how to interact with JavaScript, let's see a few gotchas in Blazor.
That's all for Part 1. Tune in tomorrow for Part 2 where we'll discuss topics just as JavaScript interoperability, writing to the console, and injecting variables.
Published at DZone with permission of Federico Tomassetti, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments