A RenderFragment
expects only one component to be returned, but that one component may have nested components:
private RenderFragment CreateRenderFragment()
{
// This works
return
@<div>
<text>Hello</text>
<text>world!</text>
</div>;
// and so does this
// return @<span><text>Hello</text> <text>world!</text></span>;
}
If we don't want to enclose the components in a <div>
or <span>
we can create a razor component following the ChildContent
convention, e.g. ChildContentComponent
:
@ChildContent
@code
{
[Parameter] public RenderFragment ChildContent { get; set; }
}
and then use this component as a wrapper:
private RenderFragment CreateRenderFragment()
{
return
@<ChildContentComponent>
<text>Hello</text>
<text>world!</text>
</ChildContentComponent>;
}
I may be mising something, but I can't see any other way to create a RenderFragment
with multiple components. Neither of the following compile:
private RenderFragment CreateRenderFragment1()
{
return @<text>Hello</text> <text>world!</text>;
}
private RenderFragment CreateRenderFragment2()
{
return
@:@{
<text>Hello</text>
<text>world!</text>
} // ; expected, but will not compile with, either
}
Is there an easier way than the ChildContentComponent
approach, or is there already a standard component which simply declares a public RenderFragment ChildContent { get; set; }
?
EDIT:
Based on MrC aka Shaun Curtis's answer below, the solution is __builder => { ... };
private RenderFragment CreateRenderFragment3()
{
return __builder =>
{
<Widget1>Hello</Widget1>
<Widget2>world!</Widget2>
};
}
Edit 2:
And as Dimitris Maragkos and H H pont out below, <text>
is a special do-nothing wrapper which is equivalent:
private RenderFragment CreateRenderFragment3()
{
<text>
<Widget1>Hello</Widget1>
<Widget2>world!</Widget2>
</text>
}
A RenderFragment
is a delegate defined like this:
public delegate void RenderFragment(RenderTreeBuilder builder);
public delegate RenderFragment RenderFragment<TValue>(TValue value);
This is my ChildContentComponent
so it stands out on the page:
<div class="bg-light m-2 p-2 border">
@ChildContent
</div>
@code
{
[Parameter] public RenderFragment? ChildContent { get; set; }
}
I think this block of code may help you. Apologies if it confuses. Any questions, ask. Components, the Renderer and RenderFragments take a while to understand.
@page "/"
<PageTitle>Index</PageTitle>
<h1>Hello, world!</h1>
Welcome to your new app.
@MyForm
@code {
// These two blocks use mixed C# and Razor markup
// it works because this is a Razor file so will be compiled by the Razor Compiler
// __builder is the RenderTreeBuilder provided by BuildRenderTree in a Razor component file
private RenderFragment MyForm => (__builder) =>
{
<ChildContentComponent ChildContent=this.ABitOfContent />
<ChildContentComponent>Another Hello</ChildContentComponent>
<ChildContentComponent>And Another!!</ChildContentComponent>
<ChildContentComponent>
@this.ABitOfContent
@this.ABitOfContent
@this.ABitOfContent
@this.AnotherBitOfContent
<ChildContentComponent>
@this.AnotherBitOfContent
</ChildContentComponent>
</ChildContentComponent>
@this.AnotherBitOfContent
};
private RenderFragment ABitOfContent = (__builder) =>
{
<div>Hello Blazor</div>
};
// This block is an example of what the Razor Compiler compiles a markup block into in the C# file
private RenderFragment AnotherBitOfContent => (builder) =>
{
builder.OpenComponent<ChildContentComponent>(0);
builder.AddAttribute(1, "ChildContent", this.ABitOfContent);
builder.CloseComponent();
};
}
You can view the C# classes that the Razor compiler emits from Razor files by adding EmitCompilerGeneratedFiles
to the project file:
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
</PropertyGroup>
</Project>
Files are emitted to:
./obj/Debug/netX.0/generated