0 Comments

32d51f27-44ec-4fa8-8801-3dc8692e3ae7[5]

This post shows you can add Blazor based pages into an existing Razor Pages application. 

Background

First RTM release of Blazor should happen in two weeks. Things are still changing quite rapidly, and the latest preview 9 made the interop between Razor Pages and Blazor components much harder: It’s not possible anymore to pass parameters from a Razor Page to a Blazor component using Html.RenderComponentAsync. This may change in future but it’s quite likely that .NET Core 3.0 will arrive with this limitation.

If you still would like to enhance your existing Razor Pages based application with some Blazor magic, one solution is completely create your pages in Blazor. This post shows you can add Blazor based pages into an existing Razor Pages application, where parts of the app are created using Razor Pages and parts of the app are created using Blazor Pages. Same layout is used for both types of pages.

Step One: Blazor support

We start with an existing Razor Pages application which has been converted to .NET Core 3:

image

First, you have to add Blazor support into your application. This support will allow you to render Blazor components from a Razor page. The official documentation goes through the process but here’s a quick rundown of it.

Startup.cs:

Services.AddServerSideBlazor is needed in ConfigureServices and endpoints.MapBlazorHub in Configure:

image

_Layout.cshtml:

Blazor’s JS-library is needed in order to enable server side Blazor. This can be added into _Layout.cshtml:

image

    <script src="_framework/blazor.server.js"></script>

_Imports.razor:

We also need a new file called _Imports.razor. This should be added into the Pages-folder:

image

_Imports.razor is used to set the using-statements for your Blazor components. We can start with the following:

@using System.Net.Http
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.JSInterop
@using Microsoft.AspNetCore.Components.Web

And that’s it.We now should have a working Blazor-support in our existing app. We can test this out by copy-pasting the classic Counter-component into our app:

image

@page "/counter"

<h1>Counter</h1>

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

<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>

@code {
    int currentCount = 0;

    void IncrementCount()
    {
        currentCount++;
    }
}

And then editing Privacy.cshtml to include the Counter component:

@page
@model PrivacyModel
@{
    ViewData["Title"] = "Privacy Policy";
}
<h1>@ViewData["Title"]</h1>

<p>Use this page to detail your site's privacy policy.</p>

<component>@(await Html.RenderComponentAsync<Counter>(RenderMode.Server))</component>

Now when we run the app, we should have a working Counter component inside our page:

9cae988e-879b-4bf5-80cf-c5622a5e0a36

Next part of this post show you can change your Razor Pages application so that instead of just adding components into existing pages, you can create full-blown Blazor pages.

Step Two: Blazor Pages support

Our Blazor component defines a route “/counter”:

image

But navigating there doesn’t work:

image

Our aim in step two is to make the routing to Blazor Pages work. And we want the Blazor Pages to use the same layout as the Razor Pages. For this we need a few things, starting with a Router.

App.razor:

Create a new App.razor file into Pages-folder:

image

The Router component is defined in App.razor:

@using Microsoft.AspNetCore.Components.Routing

<Router AppAssembly="typeof(Program).Assembly">
    <Found Context="routeData">
        <RouteView RouteData="routeData"/>
    </Found>
    <NotFound>
        <h1>Page not found</h1>
        <p>Sorry, but there's nothing here!</p>
    </NotFound>
</Router>

The router automatically goes through all the Blazor Components with the page-directive and adds routes to them.

_Host.cshtml:

We also need a page which will be used to host the Blazor pages. This can be named anyway you want but the default Blazor templates use _Host.cshtml so it’s as good name as any. In the _Host.cshtml we can define the layout which in our case will be the same as the Razor pages are using.

image

_Host.cshtml contains the call to Html.RenderComponentAsync:

@page "/blazor"

@{
    Layout = "_Layout";
}

<app>
    @(await Html.RenderComponentAsync<App>(RenderMode.Server))
</app>

Startup.cs:

And last, a small addition to Startup.cs’ Configure-method. We previously added MapBlazorHub but now we also add call to MapFallbackToPage and point it to the new _Host.cshtml:

image

And that’s it! Now we just need to test our setup. Add Blazor page Counter into your layout by editing Pages/Shared/_Layout.cshtml:

image

When we now start out application, we should have a working Blazor page in our Razor Pages application:

b8af1ec4-9549-4489-a15f-0b62e11ae2a7

And we didn’t break the support for adding Blazor components into Razor Pages:

32d51f27-44ec-4fa8-8801-3dc8692e3ae7

Notes

There’s couple things to note:

  • Blazor routes only work when they point into root. If “/counter” is changed to for example “/products/counter”, the page can’t load the required blazor.server.js. Instead it gives 404. It should be possible to modify the script-tag so that it can load the required script no matter the location but this seems to have changed from Preview 8 to Preview 9 and I couldn’t get it to work. Here’s a screenshot of the 404 showing the problem:

image

  • If you get the script to load, you probably encounter the same issues with the Blazor hub: The scripts tries to find the hub from /products/blazor instead of blazor. To get around this, you can manually start the connection between the server and the browser:
  • <script src="~/_framework/blazor.server.js" autostart="false"></script>
    <script>
      Blazor.start({
        configureSignalR: function (builder) {
          builder.withUrl('/_blazor');
        }
      });
    </script>

Sample code

Sample code for this project is available from GitHub: https://github.com/mikoskinen/blog/tree/master/blazor-pages-razor-pages-single-app