The code for this article is on GitHub: blazorapp
In 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. JavaScript is not anymoew the only language that you can use on the client, when developing for the web. It is an exciting platform, of which we already talked about in Understand WebAssembly. In this article we go one step further, and we 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 it on Blazor. In addition to that, you can obviously interact with JavaScript code, just like it is expected in a WebAssembly project.
Technically Blazor run on Mono, which is a 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 a technical information that has generally 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 technology.
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 you would want to use C# in place of JavaScript? The usual reasons 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 slogan in fact is: JavaScript that scales. C# can be an alternative solution, you 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 in some way to the user. 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 having 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 .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 distribution. 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 remains 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 would 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 behave 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
The last thing to see, to understand the standard template of a Blazor app, is 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, that 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 display the same data provided by the user in different calendars. The input, on line 9, is bind to the variable date
, while as 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.
Things to Know when Using Blazor
We have seen how simple is the general structure of a Blazor app. Now, we are going to see a few things to know when using Blazor: interoperability with JavaScript, binding details, etc.
Special Attribute to Format Strings
In our example, we bind the input to a string field and then convert the string field to the proper DateTime value. This is the typical pattern that you are going to use because the input is generally a string, while our fields are not always so. However, just for the DateTime
type, there is a special attribute that you can use to bypass the need for an intermediate variable.
<input type="date" bind="@calendarDate.Date" format-value="yyyy-MM-dd" />
We could have used the special attribute format-value
to help Blazor automatically convert to and from the DateTime
value. At the moment, this attribute only works for DateTime
variables. It works both for display and when the value is parsed after the onchange
event.
Writing to Console Works
Writing to Console works as if you were using console.log
. If we added the following line to our ConvertDate
function.
Console.WriteLine("ConvertDate called");
You would see this text on the usual Console in the browser.
The only difference is that the string is prepended with a WASM:
.
You Can Inject Variables
Using the directive inject
you can inject variables/properties into a component.
@inject HttpClient Http
Blazor comes with ready-to-inject service for HttpClient
. Since Blazor 0.5.0, you can add your custom services as in a traditional ASP.NET app, by going to the ConfigureServices
function of the Startup
class.
public class Startup { public void ConfigureServices(IServiceCollection services) { // Add any custom services here } public void Configure(IBlazorApplicationBuilder app) { app.AddComponent<App>("app"); } }
You can also use these services to perform traditional constructor injection, like in any other ASP.NET software. You can look up the documentation for more information.
JavaScript Interoperability
To see how JavaScript interoperability works, let’s create the new page Text.csthml
and add the link to navigation column in NavMenu.cshtml
. Then we create a folder js
inside www-root
, and inside this folder we add a file called site.js
.
The image shows our additions.
We obviously also need to add in a script
the JavaScript source in the main index.html
file.
Calling JavaScript from C#
Since Blazor 0.5.0, there is no need to register with Blazor the JavaScript function that we are going to call from C#. You can call any JavaScript function.
To call a registered JavaScript function we use the method InvokeAsync of JSRuntime.Current, included in the namespace Microsoft.JSInterop. The first parameter of the method is the name of the JavaScript function while the others are its arguments.The function identifier of the JavaScript function is relative to the global scope (window). For example, if you wish to call window.someFunction, the identifier is someFunction.
The generic parameter type is the return type of the function. You cannot use void or not return anything. However, there is a workaround: if you do not return anything, you can say that the function returns a string and everything works fine. Except, of course that you should not use the value returned by the invocation of the function.
We can go to the Text.cshtml
file and perform the call.
@page "/text" @using Microsoft.AspNetCore.Blazor; @using Microsoft.JSInterop; <textarea id="main_text" class="form-control" rows="10" onchange='@repeat' ></textarea> @functions { void repeat(UIChangeEventArgs e) { JSRuntime.Current.InvokeAsync<string>( "console.log", e.Value); } }
We bind the onchange
attribute to our repeat
function. Notice that the signature of the repeat function contains a parameter of type UIChangeEventArgs
. We cannot define custom parameters but we can use some that are predefined for specific events. These parameters can be omitted if we do not need them. For the change events there is an argument of type UIChangeEventArgs
that contains the new value.
Passing References of Elements
Since Blazor 0.5.0 you can also pass references of HTML elements from C# to JavaScript functions. All you have to do is adding the attribute ref
to the HTML elements and then add an ElementRef
field with the value of the attribute in the functions
section.
<textarea ref="text"></textarea> @functions { ElementRef text; }
In this example we create a reference text
for a textarea element. The ElementRef handle is completely opaque for Blazor, so you cannot do anything with itm except passing as-it-is to a JavaScript function.
@function { JSRuntime.Current.InvokeAsync<string>( "console.log", text); }
In JavaScript the reference represents the whole element. So, from JavaScript you can access its individual attributes, like id or name.
However, from Blazor you cannot pass anything other than the plain reference. So, the following code is invalid.
@function { JSRuntime.Current.InvokeAsync<string>( "console.log", // you cannot do this text.id); }
Calling C# from JavaScript
You can also call C# methods from JavaScript using the functions Blazor.invokeDotNetMethod
or Blazor.invokeDotNetMethodAsync
. However, you cannot call all methods, but only the ones that respect these conditions:
- Non-generic
- Not overloaded
- Concrete type method parameters (i.e.,
Example<string>
is valid,Example<T>
is not) - Parameters deserializable using JSON
Let’s see an example of how it works with a static method.
We create a new root C# file called Util.cs
.
namespace BlazorApp { public class Util { [JSInvokable] public static string RememberTheTruth(string text) { int start = 0; while(text.IndexOf("JavaScript", start) != -1) { start = text.IndexOf("JavaScript", start) + 10; if((text.Length > start + 4 && text.Substring(start+2, 2) != "C#") || text.Length < start + 4) text = text.Insert(start, " (C# is better)"); } return text; } } }
The class contain a static C# method decorate with [JSInvokable]
. The method checks a string and ensures that one basic truth of life is always remembered.
Then we edit the JavaScript file.
function check() { document.getElementById("main_text").value = DotNet.invokeMethod('BlazorApp','RememberTheTruth', document.getElementById("main_text").value); }
We can use the function DotNet.invokeMethod
to call a C# method (add Async if you are calling an async method):
- the first argument is the name of the assembly
- the second one is the name of the function
- the last argument is the argument for the C# method
We assign the result of our C# function call to the textarea value.
Now, we just have to change onchange
attribute of the textarea in the Text.cshtml
file to call our JavaScript function.
<textarea id="main_text" class="form-control" rows="10" onchange='check();'></textarea>
The end result works nicely.
Calling a .NET Instance Method
Since Blazor 0.5.0 you can also call .NET instance method, although the procedure is a bit more convoluted.
As usual, you have to decorate the method with [JSInvokable]
.
public class Simple { public string Text { get; set; } public Simple(string text) { Text = text; } [JSInvokable] public string ToUpper() { return Text.ToUpper(); } }
You then have to invoke the JavaScript function that is going to use that method (ToUpper
in our example) from C#, passing an object of the class to it.
@function { void shout() { JSRuntime.Current.InvokeAsync<object>( "capitalize", // name of the JavaScript function // object passed to the JavaScript function new DotNetObjectRef(new Simple("strong opinion"))); } }
The instance of the desired class is wrapped in another object of the type DotNetObjectRef
. In this example we pass an object of type Simple
.
Finally, in JavaScript we are going to use invokeMethod
to call the method we wanted to call.
function capitalize(dotnetHelper) { console.log(dotnetHelper.invokeMethod('ToUpper')); }
As you can see it is not hard, but is still a bit complex.
Summary
We have created a small example program and shown the major issues that arise when merging the C# and JavaScript world. We have seen how Blazor allows to easily bring the mature ASP.NET and C# ecosystem to the client in the browser. The interoperability with JavaScript is also there, although it is still a bit cumbersome.
Blazor is a very promising platform to deliver C# software on the client. It is still at an experimental stage, so there are a few rough edges, but it already works well enough for simple projects. Even at this stage, it is still better than using WebAssembly with C++. If you like C#, or the .NET Platform, you should start studying the platform now, since it promises to be an awesome new addition to the .NET world. However, if you really need to use C++, you can check a book: we wrote a review of WebAssembly In Action, which is written to help you work with WebAssembly from C/C++.
Blazor is a great way to fullfill the promise of WebAssembly: you can now create and bring to the client every kind of powerful and complex software that is still on the desktop. And you can do that using your favorite language and the existing codebase. What is not to like?
One thing to remember: you can call any .NET library, but you will have always to use some C# because Razor uses C#. So, you can already use F# libraries, but you need C# in the Razor pages (i.e., everything the code that is in *.cshtml
files) to make it work with Blazor.