Telerik blogs

 

See how one developer explores a newer technology and what is possible with it—in this case, how ASP.NET Core in C#, Razor and Blazor work together, including some pivots after hitting a wall.

When I began writing this blog post, I was interested in discovering how easy it is to convert a Razor cshtml to a Blazor razor file. I am rather new to Blazor, though not to Razor, and writing this piece has been instrumental in becoming more experienced.

What follows is a demonstration of my explorations of how ASP.NET Core in C#, Razor and Blazor work together, including some pivots in my research when I hit a wall. The approach I ended up using is a hybrid with Blazor components (not entire Views) embedded inside a Razor .cshtml page—which, in the end, is not an approach I would recommend for reasons of lack of elegance. However, if you are interested in learning about how one developer explores a newer technology and what is possible with it, please read on.

Before I start, and as I understand it, Blazor C# can be used on the frontend to operate instead of JavaScript, in one of its capacities. Almost instead of Razor translating C# into HTML CSS and JavaScript pre-rendering on the client.

Let us see how I learn from my research and achieve more knowledge and understanding about this relatively new addition to the ASP.NET family of languages. Further, I am interested in whether the Blazor commands render JavaScript for the browser to interpret.

One question arises as to whether it is important to learn a new language when I already know how to program JavaScript (in my own way :) ). I suppose it is nice to code in one language and let the compiler do the rest, i.e., frontend rendering.

I also understand that Blazor has further implementation usages for the server side although we are not looking at all usages. Here, we are looking at frontend.

My current level of research tends to suggest it is not possible without a lot more effort to convert an existing ASP.NET Core Razor project to a Blazor view within the same project. We do need to create two projects and transfer the relevant pages from Razor to Blazor. Mainly because I do not think it is a good idea to have a hybrid project using both types of pages. I have not researched other people’s opinions at his stage.

A week later... since writing the previous paragraph, I have changed my mind. I have incorporated the hybrid approach into the main Buku Project that I am working on. This allows Blazor “components” to be included in .cshtml files. Using Blazor “whole” pages is impractical because the “scaffolding” surrounding each page gives the program a consistent look and feel. Perhaps with considered work I could surmount this task. However, it likely would require Razor .cshtml partials to work inside Blazor (.razor) pages.

I am attempting to show you a timeline and a workflow within my learning pattern. A #core project needs to be base type Razor (.cshtml) or Blazor (.razor). After all, I did not want to explore that avenue, so I went with the hybrid model where the Blazor component can only be embedded inside a Razor .cshtml page.

Something worth noting at this point: Blazor files have the extension .razor and Razor files have the extension .cshtml. Slightly odd in my opinion—however, I expect the reason is in there somewhere.

I shall explain how I did this. I used a 2019 blog post by Mikail Koskinen which can be found here to perform the conversion. There might be a more up-to-date blog, however, this works OK (apart from a Developer Console error that has crept in).

The project I am creating uses Telerik UI for Blazor so there is a small amount extra to place in the _Imports.razor file.

@using statements include Telerik.Blazor and Telerik.Blazor.Components

The blog is pretty easy to follow and the program compiles all right, and a small Blazor component runs and is inserted correctly in a page—ipso facto the conversion worked.

Uncaught in promise syntax error: unexpected token

This seems to have crept in since I have converted the project in the way suggested by Mikail. Some research suggests that HTML is being parsed where JSON is expected. I do not think it caused a problem so I am leaving it for a while and shall look into it when I find some time.

I found some time and figured out the following. The <head> tag of the _Layout file contained the _framework script tag below. However, I moved it to the _Host.cshtml file as below (Snippet 2). I understand that the _framework line was throwing the error. Although, I think that it might still be in the wrong place... I shall research a bit more.

It seems that the _framework line needs to be placed in the cshtml page (see Snippet 1) which contains the RenderComponentAsync call. The blazor.server.js file fixes up the events for the razor (Blazor) file, among other things.

In a native Blazor project, the _framework script tag would be placed in the Master Layout file for the project so every page has this file served.

Snippet 1 (Host file for Blazor component usage—this is a test file in live project)

@model Booking.Site.Models.jCount.MassAllocationsData

@{
    this.ViewData["Title"] = "Mass Allocations";
}

@using Booking.Site.Views.jCount;
@using Microsoft.AspNetCore.Http.Features
@using Microsoft.Extensions.Caching.Memory

@inject Booking.Data.DB.Heron28.Heron28Context _oHeron28Context
@inject Booking.Data.DB.Extensions.IdentityExtend.DbContext _oDbContext
@inject IMemoryCache _oIMemoryCache

@section Header
{

    <script type="text/javascript" src="_framework/blazor.server.js"> </script>
    <script src="https://blazor.cdn.telerik.com/blazor/3.7.0/telerik-blazor.min.js" defer></script>

 }

@{

    try
    {
        if (this.Model._bOK)
        {
            if (this.User.Identity.IsAuthenticated)
            {
            }
        }
    }
    catch (Exception loException)
    {
        Booking.Library.Classes.Utility.MakeExceptionMessage(true, loException, "\r\n", "MassAllocations.cshtml");
    }
}

<script>

    function MassAllocationsTabStripSelect(e) {
        setTimeout(function () {
            ResizeAll(null);
        }, 1500);
    }

</script>

@if (this.Model._bOK)
{
    <text>
        @(Html.Kendo().TabStrip().Scrollable(true)
            .Name("MassAllocationsTabStrip")
            .Events(E => E.Select("MassAllocationsTabStripSelect"))
            .Items(items =>
            {
                items.Add()
                    .Text(Booking.Site.Classes.Helper.Text(this.Context, this._oIMemoryCache, this._oHeron28Context, this.User.Identity.Name, "Mass Allocations", "Mass Allocations", false))
                    .Selected(true)
                    .Content(@<text>
                        @(await Html.RenderComponentAsync<MassAllocations2>(RenderMode.ServerPrerendered, new { _oMassAllocationsData = Model }))
                    </text>);
                items.Add()
                        .Text(Booking.Site.Classes.Helper.Text(this.Context, this._oIMemoryCache, this._oHeron28Context, this.User.Identity.Name, "HelpInvoices", "Help Invoices", false))
                        .ImageUrl("/images/help.jpg").LinkHtmlAttributes(new { @class = "helphelp" })
                        .LoadContentFrom("_Help", "Shared", new { lnHelp = (short)Booking.Library.Classes.Enums.Screens.MassAllocations });
            })
        )
    </text>
}
else
{
    <text>
        <label>
            @Booking.Site.Classes.Helper.GetNotValidatingMessage(this.Context, this._oIMemoryCache, this._oHeron28Context, this.User.Identity.Name)
        </label>
    </text>
}
<script type="text/javascript">

    $(document).ready(function () {
        h28_SetWindowZero();
        h28_PushWindow(null, "MassAllocationsTabStrip", null, true);
        h28_SetLoadedLevel(true, null);
    });

</script>
<style>
</style>

Snippet 2 (_Host.cshtml file)

@page "/blazor"

@{
    Layout = null;
}

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

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

An example of a Blazor component is below:

Snippet 3 (Blazor component)

@page "/jCount/MassAllocations2"

@using Microsoft.AspNetCore.Components

<TelerikRootComponent>
    <table>
        <tr>
            <td>
                Hello World!
            </td>
        </tr>
    </table>
</TelerikRootComponent>

@code{
    [Parameter]
    public Booking.Site.Models.jCount.MassAllocationsData _oMassAllocationsData { get; set; }
}

And the invoking method:

Snippet 4 (Call to invoke the Blazor component)

@(await Html.RenderComponentAsync<MassAllocations2>(RenderMode.ServerPrerendered, new { _oMassAllocationsData = Model }))

Which results in this output:

Hello World

 

The equivalent of the @model feature in Razor to describe the incoming data is the declaration in the @code block. The variable is designed to await the calling method when calling the model declaration.

I cannot say I ran into too many difficulties in creating the hybrid project and now we have the option of utilizing the Blazor components where there is a case for doing so.

I like Razor but need more exposure to Blazor to estimate my liking of that product. I like the idea of Blazor, although breaking free of traditional JavaScript will be a struggle. I have invested some considerable time into learning some aspects of JavaScript.

Incidentally, the hybrid approach I mention above is unrelated to the .NET MAUI approach. The .NET MAUI approach involves having an application that can reuse components across mobile, desktop and web applications. To be more specific, the hybrid approach I mention refers to a Razor project which has enabled Blazor Component ability. Remember that a rewrite of the scaffolding around the components would be required to manage a complete integration regarding pages and components in Blazor.

This blog has been a bit strange for me. I do not always write without too much technical backup, like code snippets and the like. I suppose I am a bit out of my comfort zone talking about a technology (Blazor) that I am unfamiliar with. It is a learning curve for me, despite knowing C# well.

I hope you have enjoyed this piece of writing and the next one shall be loads more technical again. My interest in finding out how easy it was to convert a file from Razor to Blazor was not entirely satisfied. I decided to give my Razor project the ability to include Blazor components. However, converting an entire page into a substantial project is not easy, as the Layout files would require too much manipulation to make ready to receive a Blazor page.

Finally, I do not actually recommend this approach because while it uses a nice technology like Blazor and your coders might need exposure to this, perhaps an entire side project where the base type is Blazor should be used over this hybrid approach.


About the Author

David Robertson

David Robertson is a software developer/computer scientist, with degrees and many years’ experience working and writing in the field. He also enjoys ethos and philosophical discussions about computing—where he sees it going and the journey so far. He believes in balance within the tech industry and supports it at every juncture, supporting and advocating for good mental health as a way forward for reasonable and reasoned programming.

 

Related Posts

Comments

Comments are disabled in preview mode.