Playing with Blazor in .NET 5

.NET 5 was released recently, with boatloads of goodies.
Blazor got some attention in this release, and I had some free time to play with it.
There’s good news and bad news…

TL;DR #

I wanted to make something that COULD potentially be used for something and ended up with this:

AV1 support in Edge requires a Microsoft Store Extension.

What this post is and isn’t #

This isn’t to detail how to Blazor, or compare between Server and Webassembly implementations, or performance.
It’s just about File –> New Project and spinning up some basic Use Cases and how that felt.

Setup #

File –> New Project: BLAZOR #

The initial setup experience is fairly nice.
Projects are scaffolded out with useful yet minimal setup, e.g. Some basic components as a reminder of how to do things, Bootstrap included to reduce requirements on custom styling.

The finished project structure is below, and much of it was there to begin with. I just added some files.

Project Structure from template is pretty good
Project Structure from template is pretty good

Tooling is…there #

Not-so-hot reload #

Currently, there’s no hot reloading for Blazor Server :(
There is dotnet watch which watches and rebuilds on change, but it’s essentially doing a rebuild, so it’s a bit slow.
It also seems to be a bit flakey, missing/skipping changes, but that could be configuration and I didn’t explore too much.
For my simple project, the build/rebuild cycle in VS isn’t too bad, but I can imagine in large, slow-to-build projects that this would be unacceptable.

Generally flakey #

VS gives up when I rename a .razor file, requiring a “turn it off and back on again”.
This also happens every time I add a code-behind file for razor. (This may have been fixed in VS 16.8.2)
The CSS intellisense is a bit out of date (could be fixable, I haven’t dug) along with some of the built in components.
InputText is an input set to text along with extra defaults and config, but it’s intellisense doesn’t support placeholder, BUT you can just put it there and it’ll work correctly.

CSS #

Bootstrap is included by default, which can take a lot of fuss out of the first version or a spike.
Sharing CSS can probably be done a few ways, but I went with the Sass @import directive.

Scoped CSS is dope #

MyComponent.razor can have a MyComponent.razor.css and any styles in there are scoped to the component.
Is this CSS Modules? I’m not sure.

Sassy #

SASS compilation is easily supported by things like WebCompiler and Delegate.SassBuilder, anything that hooks in and compiles to CSS before Blazor gets involved.
I went with Delegate.SassBuilder as it’s a nuget package that doesn’t require IDE setup.

sassc.exe in Delegate.SassBuilder didn’t support colour transparency or sass functions and requires updating :/

Additional Setup Required #

CSS also needed additional setup out of the box.
In _Host.cshtml which manages the main html skeleton, we need <link href="MyProject.styles.css" rel="stylesheet"> to pull in those scoped modules.
The modules also need to be set to content, which can be done in csproj using blob patterns:

<ItemGroup>
  <None Remove="Pages\*.razor.css" />
  </ItemGroup>

  <ItemGroup>
  <Content Include="Pages\*.razor.css" />
</ItemGroup>

I’m not 100% why we need a None AND Content there…

SASS changes sometimes need a project rebuild as well, but not always…

Dayta/Dahta #

Databinding is available in Blazor and seems pretty cool.
It’s a bit undiscoverable though.
Below is a form:

<EditForm>
  <DataAnnotationsValidator />
  <InputText ... />
  <ValidationSummary />
</EditForm>

Those “validator” bits are key, and there’s no templates or prompts for that.

Attributes #

Validation is done through attributes on data models, shown below:

Attributes describe that property is required and needs to be at least 10 and at most 50
Attributes describe that property is required and needs to be at least 10 and at most 50

They seem fairly easy to understand and use.

Razor though… #

I find the Razor side of databinding a bit less intuitive.
Some text input examples:

<!-- This two way binds the input to the model using OnChange (i.e. only sets when unfocussed), nothing fancy -->
<InputText @bind-value="SomeModelValue" placeholder="Enter some text..." />

<!-- This two way binds the input to the model using OnInput i.e. basically keydown, but also registers keydown to something else -->  

<!-- I used this as a debounced search box. OnInput keeps the model up to date, and onkeydown does debouncing with System.Timer -->
<input @bind="@SomeModelValue" @bind:event="oninput" @onkeydown="OnTextChanged" />

There’s also onchange and @onchange… I don’t know what the first one is for, but the second one is the one that takes a delegate.

Dependency Injection #

It’s not all niggly annoyances though!
DI is supported in Razor files via @inject <serviceName> name and can then be used in the markup.
I found it most useful for NavigationManager which is how you trigger navigations from code.

Other random observations #

Code-behind, SASS, and CSS files all nested under Razor file
Code-behind, SASS, and CSS files all nested under Razor file

Conclusions #

There are many little niggles plus the massive lack of hot-reload, but ultimately it was a successful trek into Blazor.
It’s quite different to my normal web dev, so many of my frustrations were centered on not knowing Razor syntax, or how to bind properly, etc.

Does it offset not having to write Javascript?
It might, time will tell.