Say we have a basic component ParentComponent.razor:
<div id="ParentComponent">@ChildContent</div>
@code {
[Parameter] public RenderFragment ChildContent {get;set;}
}
This can be used like this:
<ParentComponent>
<div>content</div>
</ParentComponent>
Which would render:
<div id="ParentComponent"><div>content</div></div>
But say we want a ChildComponent.razor to inherit from ParentComponent.razor:
@inherits ParentComponent
<div id="ChildComponent">@ChildContent</div>
@code {
[Parameter] public RenderFragment ChildContent {get;set;}
}
Is there any way of populating ParentComponent's @ChildContent RenderFragment from the ChildComponent?
<ChildComponent>
child content
</ChildComponent>
To produce this:
<div id="ParentComponent">
<div id="ChildComponent">child content</div>
</div>
Don't know if I'm missing something obvious but been messing around with it for a while and have not been able to find any answers on the internet.
Edit - Alternative Solution
I've found another way of doing this that doesn't rely on using overriding the BuildRenderTree methods directly and allows you to use html/razor markup, although it does mean going around the houses. Also unsure if this is safe or performant, but putting it here for completeness sake.
ParentElement.razor
@using Microsoft.AspNetCore.Components.Rendering;
@if (!HasRendered()) {
SetRenderedStatus();
<div id="ParentComponent">
RenderChild(__builder);
</div>
}
@code {
protected bool _hasBaseRendered = false;
protected bool HasRendered() {
return _hasBaseRendered;
}
protected void SetRenderedStatus() {
_hasBaseRendered = true;
}
protected virtual void RenderChild(RenderTreeBuilder __builder) { }
}
ChildElement.razor
@using Microsoft.AspNetCore.Components.Rendering
@inherits ParentElement
@if (HasRendered()) {
<div id="ChildComponent">
@ChildContent
</div>
}
@{
base.BuildRenderTree(__builder);
}
@code {
[Parameter] public RenderFragment ChildContent { get; set; }
protected override void RenderChild(RenderTreeBuilder __builder) {
this.BuildRenderTree(__builder);
}
}
So quick explanation, the features in the IDE/Compiler clearly compile the razor/html markup portion of the code down into BuildRenderTree methods behind the scenes, which is why you can't override those methods within a .razor component.
The above code compiles down to:
ChildElement.razor
protected override void BuildRenderTree(RenderTreeBuilder __builder)
{
if (HasRendered())
{
__builder.AddContent(0, " ");
__builder.OpenElement(1, "div");
__builder.AddAttribute(2, "id", "ChildComponent");
__builder.AddMarkupContent(3, "\r\n ");
__builder.AddContent(4, ChildContent);
__builder.AddMarkupContent(5, "\r\n ");
__builder.CloseElement();
__builder.AddMarkupContent(6, "\r\n");
}
base.BuildRenderTree(__builder);
}
So the ChildContent is ignored while HasRendered is false and it is directed to call the base.BuildRenderTree on the parent class.
ParentElement.razor
protected override void BuildRenderTree(RenderTreeBuilder __builder)
{
if (!HasRendered())
{
SetRenderedStatus();
__builder.AddContent(0, " ");
__builder.OpenElement(1, "div");
__builder.AddAttribute(2, "id", "ParentComponent");
__builder.AddMarkupContent(3, "\r\n");
RenderChild(__builder);
__builder.AddContent(4, " ");
__builder.CloseElement();
__builder.AddMarkupContent(5, "\r\n");
}
}
It renders the parents markup and midway is directed to RenderChild which is overriden on the child, that takes the flow back up a level to the child, where its BuildRenderTree is then called, the HasRendered check will ensure an infinite loop is avoided and it then renders the childs portion of the markup before returning back down to the parent to complete.
This could probably be refactored, just my current working copy experiment.
