microsoft blazor

Solve your problems and get started with Microsoft Blazor- dynamic content

Hi all, let me demonstrate the amazing technology that Microsoft released a few years ago as a part of ASP.NET Core – Blazor!

If you ask me why I love this technology I will answer – well, it’s because I hate JavaScript (like many others), and this time Microsoft delivered something so useful and powerful for web developers, that now it becomes a real alternative to JavaScript and all these bloatware frameworks created to extend its pointless life (Angular, React, etc.)

Working at Pro Coders we used Blazor from its preview release and tried many amazing things, mostly with Blazor Server-Side that is built on top of SignalR. I also should mention that the technique I am going to share today can be used for client-side Blazor WebAssembly too.

Dynamic Content

As you may know, to define Blazor UI we use Razor pages – technology that was around for about 10 years, and I will not waste your time digging into Razor, assuming it is simple and well known for you.

What happens if you don’t know how your page should look at development time, and its content and page controls should be shown based on data that can be changed dynamically by a user or a site administrator? You need to generate your page dynamically.

Let’s talk about an example from real life- content management systems where the site administrator can decide which fields can be populated by a user in the user profile page and let me formulate a few requirements that we will implement today.

Requirements: dynamic UI generation

  • Generate a UI page based on a control list received from a service
  • Support two types of controls: TextEdit and DateEdit
  • Control list have properties for UI generation: Label, Type, Required

Implementation – create a project

Let’s create a new Blazor project and let’s keep it simple. Open Visual Studio 2019 and click [Create a new project], then find Blazor template and click [Next]:

3. Enter the project name

4. Select [Blazor Server App] on the next page, and click [Create]:

blazor

5. You will see a new solution created for you and it will contain several pages that the Visual Studio template added for learning purposes.

6. You can build and run the solution to see the created application in a browser, by clicking on the play button (green triangle).

blazor .net

Implementation- Define model and service

I prefer starting from the defining model, so create a new class [ControlDetails.cs] and put the following code into it:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace DemoDynamicContent
{
    public class ControlDetails
    {
        public string Type { get; set; }
        public string Label { get; set; }
        public bool IsRequired { get; set; }
    }
}

Now we can create a service class that, for now, will return some test data, so create [ControlService.cs] and add this code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace DemoDynamicContent
{
    public class ControlService
    {
        public List<ControlDetails> GetControls()
        {
            var result = new List<ControlDetails>();
            result.Add(new ControlDetails { Type = "TextEdit", Label = "First Name", IsRequired = true });
            result.Add(new ControlDetails { Type = "TextEdit", Label = "Last Name", IsRequired = true });
            result.Add(new ControlDetails { Type = "DateEdit", Label = "Birth Date", IsRequired = false });
            return result;
        }
    }
}

In this code, we specify three controls that we want to show on our dynamic page: First Name, Last Name, and Birth Date.

The last bit that we need to do is to register our service for Dependency Injection, so simply open [Stratup.cs], locate [ConfigureServices(IServiceCollection services)] method and add a line of code at the bottom, so it looks like :

 public void ConfigureServices(IServiceCollection services)
        {
            services.AddRazorPages();
            services.AddServerSideBlazor();
            services.AddSingleton<WeatherForecastService>();
            // added line for ControlService
            services.AddSingleton<ControlService>();
        }

Implementation- Dynamic page

Let’s reuse [Counter.razor] page (already created by Visual Studio template) for simplicity.
We need to delete all lines except the first one and start adding our own code, firstly use Dependency Injection to inject our service:

@page "/counter"
@inject ControlService _controlService

Now we need to execute _controlService and iterate through the returned list of controls.

@page "/counter"
@inject ControlService _controlService
@foreach (var control in _controlService.GetControls())
{
}

For each control we will want to show a label that is marked by * if it is a required control:

@page "/counter"
@inject ControlService _controlService
@foreach (var control in _controlService.GetControls())
{
    @if (control.IsRequired)
    {
        <div>@(control.Label)*</div>
    }
    else
    {
        <div>@control.Label</div>
    }
}

The final bit is to render control of a particular type in a [switch] statement:

@page "/counter"
@inject ControlService _controlService


@foreach (var control in _controlService.GetControls())
{
    @if (control.IsRequired)
    {
        <div>@(control.Label)*</div>
    }
    else
    {
        <div>@control.Label</div>
    }


    @switch (control.Type)
    {
        case "TextEdit":
            <input required="@control.IsRequired">
            break;


        case "DateEdit":
            <input required="@control.IsRequired" type="date">
            break;
    }
}

Implementation- Running

Now you can compile and run your solution.
On the appeared browser window click [Counter] menu item to see the result:

dynamic content

Implementation- Adding control binding

Extending this idea further we will need to bind generated razor controls to properties in our razor page and store them in a Dictionary for example, where Key is Label and Value is the razor control value.

Here I added the [@code] section that has service execution logic and all the properties and events that we bind to controls. The bindings work in both directions.

The resulting code will look like:

@page "/counter"
@inject ControlService _controlService
@foreach (var control in ControlList)
{
    @if (control.IsRequired)
    {
        <div>@(control.Label)*</div>
    }
    else
    {
        <div>@control.Label</div>
    }
    @switch (control.Type)
    {
        case "TextEdit":
            <input @bind-value="@Values[control.Label]" required="@control.IsRequired" />
            break;
        case "DateEdit":
            <input type="date" value="@Values[control.Label]" @onchange="@(a => ValueChanged(a, control.Label))" required="@control.IsRequired" />
            break;
    }
}
<br/>
<button @onclick="OnClick">Submit</button>
@code
{
    private List<ControlDetails> ControlList;
    private Dictionary<string, string> Values;
    protected override async Task OnInitializedAsync()
    {
        ControlList = _controlService.GetControls();
        Values = ControlList.ToDictionary(c => c.Label, c => "");
    }
    void ValueChanged(ChangeEventArgs a, string label)
    {
        Values[label] = a.Value.ToString();
    }
    string GetValue(string label)
    {
        return Values[label];
    }
    private void OnClick(MouseEventArgs e)
    {
        // send your Values
    }
}

If you run your solution now, having a breakpoint in the [OnClick] method, then enter values in page controls, and click the [Submit] button you will see the entered values in the watch panel at the bottom:

visual studio

It is brilliant, we store entered values in a Dictionary and now can supply it to a service that saves values to a database.

The full solution with the resulting code can be found on my GitHub page:

https://github.com/euklad/BlogCode/tree/main/DemoDynamicContent-story1

Summary

In this exercise, we implemented a UI page that generates controls using data received from a service. The Data controls the Presentation. Supplying data stored in a database we present content to users. If we want to present slightly different content – we need to only change the data in our database and users see our changes, no recompilation, or deployment required.

But this solution has a small limitation – it will support only those controls that we specify in the [switch] statement on the razor page. Each time we need to show a control that is not specified in the [switch] statement we need to extend it, adding code with new control and recompile solution.

Next Challenge

Custom controls for dynamic content

Is it possible to create an externally extendable dynamic page, which will support all controls that we can add later in a separate assembly without the recompilation of our dynamic page?

Yes, it is possible – using a technique that I will share in my next blog.

https://procoders.com.au/microsoft-blazor-custom-controls-for-dynamic-content/

Thank you and see you next time!