Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

BuildRenderTree is not called in a razor page that inherits of my custom component

I have the following files:

BaseComponent.cs:

using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Rendering;

namespace Test.Pages;

[Route("/basepage")]

public class BasePage :ComponentBase
{
    protected override void BuildRenderTree(RenderTreeBuilder builder)
    {
        base.BuildRenderTree(builder);
        var seq = 0;
        builder.OpenElement(seq, "h3");
        builder.AddContent(++seq, "BuildRenderTree of BasePage.cs");
        builder.CloseElement();
    }
}

CustomNonRazorPage.cs:

using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Rendering;

namespace Xcition.Client.Pages;

[Route("/customnonrazorpage")]
public class CustomNonRazorPage : BasePage
{
    protected override void BuildRenderTree(RenderTreeBuilder builder)
    {
        base.BuildRenderTree(builder);
        var seq = 0;
        builder.OpenElement(seq, "h3");
        builder.AddContent(++seq, "BuildRenderTree of CustomNonRazorPage.cs");
        builder.CloseElement();
    }
}

CustomPage.razor:

@inherits BasePage
@page "/custompage"

<h3>CustomPage.razor</h3>

I call the following relative URI in the browser:

  • /basepage: returns a page with BuildRenderTree of BasePage.cs OK, it calls its own BuildRenderTree method
  • /customnonrazorpage: returns a page with BuildRenderTree of BasePage.cs and BuildRenderTree of CustomNonRazorPage.cs OK, it calls the BuildRenderTree method of BasePage
  • /custompage: returns a page with CustomPage.razor Unexpected value because it doesn't call the BuildRenderTree method of BasePage

May be I'm doing something wrong because I'm a beginner in Blazor.

I would like to create my custom base component that surrounds with an another component the body of a child razor component that inherits of my custom base component (something like the Layouts do).

I try to explain it better with an example:

  • FinalComponent.razor that inherits from BaseComponent and with a body like "My final content".

  • BaseComponent.cs that implements in BuildRenderTree the code that surrounds [Parameter] RenderFragment body with the OtherComponent

  • OtherComponent.razor that contains <div>@Body</div>

I would like to get the final result like this:

<div><h3>My final content</h3></div>

The target is to be able to decide by code in the BaseComponent what is the the class that I would use for the OtherComponent without need to know it from the FinalComponent.

like image 374
Asier Cayón Francisco Avatar asked Oct 16 '25 10:10

Asier Cayón Francisco


2 Answers

Welcome to the world of ComponentBase.

Unfortunately ComponentBase wasn't built with "Wrapper" functionality in mind, so making it perform that trick is a little convoluted. The alternative, which I don't recommend when you're new to Blazor, is build your own base component that will support wrapper functionality "out of the box". There are references to articles that explain hoe to do this at the bottom of the article.

The problem with CustomPage is that:

<h3>CustomPage.razor</h3>

is compiled by the Razor compiler into a protected override void BuildRenderTree(RenderTreeBuilder builder) method that overrides yours in the parent component.

To make ComponentBase wrap you need to create and manipulate some RenderFragmnents.

Here's my demo MyTemplate. It's in your face red so you can see it's wrapping.

It consists of two readonly RenderFragment properties. Wrapper is the actual content you want to wrap around the content provided by the child content in Content.

@code {
    protected RenderFragment Wrapper => __builder =>
        {
            <div class="bg-danger m-2 p-1">
                <h3>MyTemplate</h3>
                @this.Content
            </div>
        };

    protected virtual RenderFragment? Content { get; }
}

Now our Child - in this case Index. The real content goes into the overidden Content. The content that gets build into BuildRenderTree is the parent wrapper RenderFragment defined in Wrapper.

@page "/"
@inherits MyTemplate

@Wrapper

@code {
    protected override RenderFragment Content => __builder =>
        {
            <PageTitle>Index</PageTitle>

            <h1>Hello, world!</h1>

            <span>Welcome to your new app.</span>

            <SurveyPrompt Title = "How is Blazor working for you?" />
    };
}

The result: enter image description here

A NONO

Don't do this in a builder.

        var seq = 0;
        builder.OpenElement(seq, "h3");
        builder.AddContent(++seq, "BuildRenderTree of BasePage.cs");

Do this:

        builder.OpenElement(1, "h3");
        builder.AddContent(2, "BuildRenderTree of BasePage.cs");

See - https://learn.microsoft.com/en-us/aspnet/core/blazor/advanced-scenarios?view=aspnetcore-7.0#sequence-numbers-relate-to-code-line-numbers-and-not-execution-order

For those wishing to build a base component here are links to two [of my] articles on the topic:

  • https://shauncurtis.github.io/Building-Blazor-Applications/Building-Wrapper-Components.html
  • https://shauncurtis.github.io/Articles/The-Three-Component-Solution.html
like image 106
MrC aka Shaun Curtis Avatar answered Oct 18 '25 01:10

MrC aka Shaun Curtis


It is possible, essentially in the same way as in your CustomNonRazorPage

@inherits BasePage
@page "/custompage"

@{ base.BuildRenderTree(__builder); }

<h3>CustomPage.razor</h3>

but besides being ugly that __builder is a hack, no warranties.

like image 30
Henk Holterman Avatar answered Oct 18 '25 00:10

Henk Holterman