Monthly Archives: December 2024

Blazor templates

Way back I wrote the post Blazor Components. This post demonstrated how to create simple components within Blazor. I didn’t progress massively, using Blazor back when that post was written, but I’ve been getting back into Blazor recently.

Let’s look at a powerful feature of Blazor (a little bit like WPF lookless controls) in that we can create Blazor templates using code behind with entry points (placeholders) for the UI to interact with.

One of the obvious uses of such a template might be for lists or grids etc. as these might allow us to add a header and/or footer along with rows which have the same look and feel but with different data.

Let’s look at a very simple example, a ListView template which allows us to output a list of rows where the list (at a top level) can be styled by the code using it and each row and be styled as well. Ofcourse you could style things quite easily if you know the CSS class etc. but here I mean styled as in you can change the UI itself, hence let’s begin by looking at how we could change a ListView to use ordered lists or unordered lists.

Note: I’m going to reuse the weather forecast data supplied when creating a Blazor WebAssembly application in Visual Studio

The component

First off, let’s create a component named ListView, I’m creating it within a folder named Components. The ListView.razor look like this

@typeparam TItem

@if (ListTemplate is not null)
{
    @ListTemplate(
        @:@{foreach (var item in Items) 
            {
                @ItemTemplate(item)
            }
          }
    )
}

We’re declaring a typeparam named TItem as this adds generic type parameter support which you’ll see being used in the code behind file. We can add a constraint to this type if required, but for this example we’re not too concerned about what type TItem takes. In the example above we’re assuming the user will supply the ListTemplate otherwise nothing is displayed, ofcourse we could create a default output if we wished or display a message for the developer.

The code behind, the ListView.razor.cs looks like this

using Microsoft.AspNetCore.Components;

namespace BlazorTemplates.Components;

public partial class ListView<TItem>
{
    [Parameter] public RenderFragment<RenderFragment>? ListTemplate { get; set; }
    [Parameter] public RenderFragment<TItem> ItemTemplate { get; set; } = default!;
    [Parameter] public IReadOnlyList<TItem> Items { get; set; } = default!;
}

What’s happening here is that we’re declaring a ListTemplate which is a RenderFragment, which is simple put, a piece of UI content, the generic type is also a RenderFragment – so ListTemplate is UI content of UI content essentially. This will act as our “outer” UI, so for example, if were using the ListView for ordered list items, then this would contain the <ol></ot> section of the UI.

The ItemTemplate is again a RenderFragment, hence UI content supplied by the calling code, but this type it takes a generic TItem type, this refers to the type of the list items themselves, i.e. for the WeatherForeast sample code, this is a WeatherForecast class/type. Hence the ItemTemplate will be passed an item from the supplied Items list (within our template) and allows the calling code to render each item.

The Items property is what’s used by the calling code to supply the list of data to our template.

Calling the component

If we look again at the .razor code you’ll see that really all we’re doing it calling the ListTemplate and passing through a segment of code using Wig-Pig syntax which creates a RanderFragment. This then gets used to call through to the ItemTemplate and this template is passed each item from the list of Items so that we can render each row, let’s see the code in use

<ListView Items="_forecasts">
  <ListTemplate Context="rows">
    <ul>@rows</ul>
  </ListTemplate>
  <ItemTemplate Context="forecast">
    <li>@forecast.Summary</li>
  </ItemTemplate>
</ListView>

Note: _forecasts is the same as the Weather page in the default Blazor WebAssembly template, hence an array of WeatherForeast items

In the example above we’re renaming the context for use within each template, we could have just used the following if we preferred

<ListView Items="_forecasts">
  <ListTemplate>
    <ul>@context</ul>
  </ListTemplate>
  <ItemTemplate>
    <li>@context.Summary</li>
  </ItemTemplate>
</ListView>

As can be seen (which ever version you use) then ListTemplate is creating the “outer” element, then passing the context into the RenderFragement we supplied within the ListView.razor file, which itself then loops through each item with the Items list (which was defined on the ListView Items property in the above code.

The template then calls the ItemTemplate for each item and the template for this is supplied in the ItemTemplate fragment in the above code. In this case we using li and displaying the summary for each weather forecast.

Using the template if different scenarios

Now using the template to display an unordered list is great, and yes it’s obvious how we can easily change the ol to an ul but I’m sure this looks like a lot of effort for something very easy in Blazor anyway. So let’s look at extending this into something a little more useful, but before we do that, here’s the example of using this template with ordered lists, unordered lists and tables

<ListView Items="_forecasts">
  <ListTemplate>
    <ul>@context</ul>
  </ListTemplate>
  <ItemTemplate>
    <li>@context.Summary</li>
  </ItemTemplate>
</ListView>

<ListView Items="_forecasts">
  <ListTemplate>
    <ol>@context</ol>
  </ListTemplate>
  <ItemTemplate>
    <li>@context.Summary</li>
  </ItemTemplate>
</ListView>

<ListView Items="_forecasts">
  <ListTemplate>
    <table>@context</table>
  </ListTemplate>
  <ItemTemplate>
    <tr>@context.Summary</tr>
  </ItemTemplate>
</ListView>

How about we extend our template to allow for a header and a footer, hence allowing us to use more of the table functionality but also have the same functionality now for the ordered and unordered lists.

We can simply add the following to our ListView.razor.cs class

[Parameter] public RenderFragment? HeaderTemplate { get; set; }
[Parameter] public RenderFragment? FooterTemplate { get; set; }

and within the ListView.razor file just add HeaderTemplate and FooterTemplate like this

@typeparam TItem

@if (ListTemplate is not null)
{
    @HeaderTemplate

    @ListTemplate(
        @:@{foreach (var item in Items) 
            {
                @ItemTemplate(item)
            }
          }
    )

    @FooterTemplate
}

All that’s left to do is add HeaderTemplate code to our calling code, so for example the table based version would look like this (I’ve added more table like element/attribute parts to this code)

<ListView Items="_forecasts">
  <HeaderTemplate>
    <thead><tr><th scope="col">Summary</th></tr></thead>
  </HeaderTemplate>
  <FooterTemplate>
    <tfoot><tr><th scope="row">Footer</th></tr></tfoot>
  </FooterTemplate>
  <ListTemplate>
    <table>
      <caption>This is a table</caption>
      <tbody>@context</tbody>
     </table>
  </ListTemplate>
  <ItemTemplate>
    <tr scope="row">@context.Summary</tr>
  </ItemTemplate>
</ListView>

Now we can also add a header and footer to our ordered and unordered lists in the same way, i.e.

<ListView Items="_forecasts">
  <HeaderTemplate>
    <div>Summary</div>
  </HeaderTemplate>
  <FooterTemplate>
    <div>Footer</div>
  </FooterTemplate>
  <ListTemplate>
    <ul>@context</ul>
  </ListTemplate>
  <ItemTemplate>
    <li>@context.Summary</li>
   </ItemTemplate>
</ListView>

Note: ofcourse this is not very pretty, so I’ll leave it to the reader to create some nice CSS for the header and footer divs

That’s pretty much it – templates can be useful if you’re going to abstract some UI pattern in a reusable way.

Code for this post is available on GitHub.

Blazor and TypeScript

In the past I wrote a blog post on Blazor and the JavaScript interop. but what about TypeScript, I mean it obviously transpiles to JavaScript, so we should be able to use it, right ?

So yes, we can easily use TypeScript within a Blazor application, let’s do the following

  • Create a Blazor WebAssembly Standalone App (you can use the server one’s if you prefer, but for this post that’s the template I’m starting with)
  • Add the NuGet Package Microsoft.TypeScript.MSBuild, this will allow us to transpile our TypeSript code as part of the build process. This will add the following to the csproj (the version may differ ofcourse)
    <PackageReference Include="Microsoft.TypeScript.MSBuild" Version="5.7.1">
      <PrivateAssets>all</PrivateAssets>
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
    </PackageReference>
    
  • Within the project root, i.e. where Program.cs is located, add a file tsconfig.json, mine looks like this
    {
      "compilerOptions": {
        "module": "ES2015",
        "target": "ES2024",
        "sourceMap": true
      },
      "exclude": [
        "node_modules"
      ]
    }
    
  • Let’s create a scripts folder off of wwwroot
  • Now create a file, mine’s not very imaginatively named Example.ts and here’s the code
    namespace Example {
        export class Prompt {
            public showAlert(message: string): string {
                return prompt(message, "Hey");
            }
        }
    }
    
    export function getPromptInstance(): Example.Prompt {
        return new Example.Prompt();
    }
    

Before we look at using this code let’s just review a few of the steps.

The tsconfig.json if, ofcourse, used to configure the TypeScript transpiler. The module and target do NOT need to be these two values, but we do need to use a module type which will generate an export function. Some of the other module types will not include the export definition and then we cannot access the factory method getPromptInstance. So feel free to change these two options but before your JavaScript needs to export our factory function.

As mentioned, we’re using the getPromptInstance to create an instance to our class, ofcourse we could export more functions and remove the class in this example.

Interop from Blazor

Interop. with our transpiled code uses the same mechanism/code as my old post Blazor and the JavaScript interop., but let’s create some code in this sample anyway.

Let’s do the following

  • Add a Button to the Home.razor page that looks like this
    <button class="btn btn-primary" @onclick="DisplayMessage">Alert me</button>
    
  • Now add a code block which looks like this
    @code{
        private async Task DisplayMessage()
        {
            var module = await JsRuntime.InvokeAsync<IJSObjectReference>("import", "./scripts/Example.js");
            var o = await module.InvokeAsync<IJSObjectReference>("getPromptInstance");
            await o.InvokeVoidAsync("showAlert", "Hello TypeScript World");
        }
    }
    
  • We also need to add the following to the top of the Home.razore file, below the @page line
    @inject IJSRuntime JsRuntime
    

Now when you run the application you’ll see the “Alert me” button, clicking this will load the script (note we reference the .js script here obviously) and then we get a reference to the getPromptInstance function and call the showAlert method of the previously defined TypeScript class.

Obviously you’ll probably prefer to create a new C# class that holds onto a reference to the imported script, but hopefully this post gives you a starting point to porting/using TypeScript code within your Blazor app.

Code for this post is available at GitHub