Home > Software engineering >  Factoring out markup in Blazor
Factoring out markup in Blazor

Time:07-30

Repeated code

In my application, I had code like this that was repeated in a bunch of places:

@if (economy.Cash == economy_base.Cash)
{
    <span>
        "Cash" @(String.Format("{0:N2}", economy.Cash))
    </span>
}
else
{
    <span>
        Cash @(String.Format("{0:N2}", economy_base.Cash))
        <span style="color: purple;">
            (@(String.Format("{0:N2}", economy.Cash)))
        </span>
    </span>    
}

where economy.Cash, economy_base.Cash and "Cash" were values that were different in each case.

Factoring it out into a function

At first, I looked into using the technique described here that works for ASP.NET Core applications:

https://stackoverflow.com/a/67656536/268581

However, the exact approach there didn't seem to work in Blazor.

So instead of using a function signature of async Task Template(...) I used void Template(...). And this seems to have worked!

Given this:

@{
    void Template(string label, decimal? a, decimal? b)
    {
        @if (a == b)
        {
            <span>
                @label @(String.Format("{0:N2}", a))
            </span>
        }
        else
        {
            <span>
                @label @(String.Format("{0:N2}", b))
                <span style="color: purple;">
                    (@(String.Format("{0:N2}", a)))
                </span>
            </span>    
        }
    }
}

I can use the following:

@{
    Template("Cash", economy.Cash, economy_base.Cash);
}

and it'll expand into the example code shown above in the first section.

Question

When defining Template I just took a wild guess and tried the void Template(...) signature.

My question is, is this a valid and recommended way to factor out Blazor markup into a function?

CodePudding user response:

My question is, is this a valid and recommended way to factor out Blazor markup into a function?

There is nothing inherently wrong with it, but be aware that this is not a normal Template.

What you have here is a local function inside the override BuildRenderTree() that is generated from the markup section. Hence the need to surround the calls with @ { }.

Your other options are

  • a RenderFragment<T> Template, but that would require that you bundle string label, decimal? a, decimal? b into one Type somehow.
    Usage @Template(new MyType("Cash", economy.Cash, economy_base.Cash)), without extra @{}

  • a normal Blazor Component (separate .razor file).
    Usage would look like <Template Label="Cash" A="economy.Cash" B="economy_base.Cash" />

CodePudding user response:

My question is, is this a valid and recommended way to factor out Blazor markup into a function?

This may be a valid way to do that, but it is not the way things are done in Blazor. The recommanded way or rather a good practice is to use the RenderFragment delegate type, which was invented to render partial markup...

There are plenty of ways to implement your function... Here's one:

Copy and test...

@page "/"

@Template(("my label", valA, valB))

@code
{
    decimal? valA = 2.34M;
    decimal? valB = 3.34M;

    private RenderFragment<(string label, decimal? a, decimal? b)> Template => value => __builder =>
    {
        @if (@value.a == @value.b)
        {
        <span>
            @value.label @(String.Format("{0:N2}", @value.a))
        </span>
        }
        else
        {
        <span>
            @value.label @(String.Format("{0:N2}", @value.b))
            <span style="color: purple;">
                (@(String.Format("{0:N2}", @value.a)))
            </span>
        </span>
        }
    };
}

Note: It is a good practice to use the RenderFragment delegate in place of Rqazor components. Thus, and solution to implement your code through components is a very bad thing to do. Components are very expensive, and must not be used for the sole purpose of rendering content.

  • Related