Category Archives: Blazor

Blazor routing and Navigation

Routing

If you take a look at the generated pages for a Blazor application, you’ll see the following

@page "/counter"

this denotes a route to our page, i.e. http://localhost:44319/counter.

Note: When you compile your application a file is generated (see Counter.g.cs in compiled folder) and this page declaration is converted to a RouteAttribute.

You can have multiple routes pointing to the same page, in this example all these routes will navigate to the same Counter.razor page

@page "/counter"
@page "/mycounter"
@page "/something"

Probably more useful is the ability to add parameters to our routes, for example

@page "/counter"
@page "/counter/{InitialValue}"

By default InitialValue is of type string, but we can change this by using a constraint, for example

@page "/counter"
@page "/counter/{InitialValue:int}"

Now we simply create a property in our code with the ParameterAttribute applied to it, like this

[Parameter]
public int? InitialValue { get; set; }

We use a nullable int in case the value is missing. We can also have routes handling different type contraints (for example a string or on int) to mean two different things

@page "/counter"
@page "/counter/{InitialValue:int}"
@page "/counter/{SomeString}"

In our OnInitialized() method (part of the component life cycle) we might then set the currentCount to InitialValue.

The routes are declared in our pages but the routing component itself can be found in App.razor. In this case the Router will locate the routes from the supplied AppAssembly

<Router AppAssembly="@typeof(Program).Assembly">

We can also includes routes within AdditionalAssemblies, for example maybe you’ve got some reusable components with routing supplied, hence we write

<Router AppAssembly="@typeof(Program).Assembly" 
   AdditionalAssemblies="new[] { typeof(MyComponents).Assembly }>

Navigation

If you want to navigate to other pages, we need to use the NavigationManager. This needs to be injected into our page, using

@inject NavigationManager NavManager

Now to navigate, for example when a button is click, we use the following

NavManager.NavigateTo("counter/2");

In the instance we’ll navigate to the relative URL/page with “counter/someData”.

This works fine when navigating to other pages but if we want to navigate within a page and we’re using the component’s OnInitialized method to initialize data?

Because OnInitialized is called when the page is initial loaded and because we’re already in the page, this method will not normally be called again if we navigate within the page, so we simply need to supply an extra parameter to our NavigateTo code to force the page to reload, i.e.

NavManager.NavigateTo("counter/2", true);

Note: An alternative solution ofcourse is to override the other life cycle method OnParameterSet and instead move initialization code to that method.

Adding imports to your Blazor app.

The default template’s supplied for Blazor Server and WebAssembly applications, gives you lots upfront. However, ofcourse you’re likely to want to import further functionality via the @using directive.

For example, let’s say we want to add the following to the Counter.razor IncrementCount method (from the default template generated files)

Debug.WriteLine("Increment");

We can simply add the following to the top of the Counter.razor file

@using System.Diagnostics

Alternatively and especially useful for using code across multiple pages/components, you can put the above code into the _Imports.razor file. Think of this as a global set of using reference, but only for @code sections, for standard C# classes we ofcourse use the using directive as usual.

Note: I’ve found my current version of Visual Studio doesn’t seem to immediately recognise the updated _Imports.razor file, a build fixes that.

Getting started with Blazor on Server and WebAssembly

As of Visual Studio 16.6.3 and .NET core 3.1.301 there’s now support for create Blazor applications for Server and WebAssembly.

Creating your project

From Visual Studio

  • Create a new project and look for the Blazor App template
  • You’ll be presented with the options to create a Blazor Server App or Blazor WebAssembly App

From the dotnet CLI

  • To create a server app run
    dotnet new blazorserver -o your-app-name
    
  • To create a WebAssembly app run
    dotnet new blazorwasm -o your-app-name
    

You can also run the generated code by changing directory to the your-app-name and then execute the following

dotnet run

Whichever route you took to generate your Blazor app. When you run the application, you can access the application via https://localhost:5001/.

Blazor Server

I’m not going to cover Blazor on the server specifically, as this is very much like Blazor WebAssembly crossed with ASP.NET core development. Basically what you write for WebAssembly is very much the same as you’d write for the Server.

There are benefits and downsides to Blazor Server and Blazor WebAssembly, I’m not going to list them all (because I’m sure I’ll forget one or two) but basically running Blazor on the server gives faster start-ups, allows us to be more secure with our source code (in that the code is not available to be downloaded but runs instead on the server). However the Server implementation suffers from the lack of an offline mode (i.e. PWA is supported via Visual Studio for Blazor WebAssembly) and there’s bound to be more network traffic with a server implementation.

Blazor WebAssembly

WebAssembly or WASM is a binary instruction set for browsers (well those that support it). Think of it as a virtual machine for running byte code. The important parts of WASM are that any it can run code generated from C# (other languages are adding support) instead of just using JavaScript for the Web. The second thing of interest is that the virtual machines are sandboxed.

It might sound like we’ve sort of been here before, and we have to a degree, but this time we have an open standard instead of plugins, Java applets, Silverlight or ActiveX.

With Blazor WebAssembly the compiled code is executed on the client, within the browser as a SPA. With Blazor Server, much like ASP.NET, the code comes from the server, however clever use of SignalR allows diffs to/from the DOM to be sent back and forth in an efficient manner.

Looking into the Blazor templates

If we look at both the WASM and Server code we’ll see our pages written in Razor syntax, here’s the Counter.razor file

@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++;
    }
}

Razor syntax is denoted by the @ prefixed commands, for example @page is basically a routing command to this counter page. @code is a C# code block. Also we can use variables from our code by prefixing with the @ (such as @currentCount).

When you see a @code block, think of it as a class, hence in this example we have a field currentCount and a member method named IncrementCount.

Binding

One way binding is very simple (and shown in the above snippet)

<p>Current count: @currentCount</p>

In this case the variable is updated via the @code and one way binding ensures that the text in the HTML is updated automatically for us.

Ofcourse we’re going to need two-way binding and this is pretty simple in Blazor. Let’s add an input to the Counter.razor code (just place above the button HTML).

<input @bind="currentCount" />

This input will by default, bind the variable currentCount to it’s value property, we can also write explicit code for this in this way

<input @bind-value="currentCount" />

You use the syntax @bind-{Property} replacing {Property} with whatever the property or attribute name is that you wish to bind to.

What happens now is that, when the user changes the input value and the control loses focus, the currentCount is automatically set to the new value. We may want to instead have the value update as the input changes, in this instead we use @bind-value:event, for example

<input @bind-value="currentCount" @bind-value:event="oninput"/>

Note: if you put a char or string into the input, the binding doesn’t update and hence you get no exceptions or anything.

Blazor Components

Each page that we have is basically a Blazor component, but we can create a component that’s not a page – in other words it’s just a component and will be used to render HTML fragments.

From Visual Studio, right mouse click on the Shared folder and then select Add | Razor Component. Mine’s named Count.razor and the aim is to move the code into it’s own component

<p>Current count: @currentCount</p>

I called my component Count.razor. Let’s change the Counter.razor page to use this new component, so replace the code and above with

<Count @bind-Counter="currentCount"></Count>

Here’s our Count.razor component code

<p>Current count: @Counter</p>

@code {
    [Parameter]
    public int Counter { get; set; }

    [Parameter]
    public EventCallback<int> CounterChanged { get; set; }
}

Whilst we’re not using the CounterChanged event, it’s required for the binding code. The ParameterAttribute is basically exposing properties to the parent components or pages, i.e. for use outside of the component itself.

Now I’m not 100% on this code (i.e. CounterChanged not being used) as it worked fine, but I was finding Visual Studio displays error tildes under the currentCount usage in the Counter page, although everything worked. I’m not sure if this is just an issue where Visual Studio it not yet upto date with all the syntax or a mistake on my part – although it does work.

However, if we just change the page’s code to

<Count @bind-Counter="currentCount" 
   @bind-Counter:event="CounterChanged"></Count>

then no error tildes were displayed in Visual Studio – this needs further investigation as it seems a bit superfluous adding this event when it’s not used – but hey, I’m new to Blazor!

That’s it for Getting Started.

References

Razor syntax reference for ASP.NET Core
WebAssembly
Introduction to ASP.NET Core Blazor