So, I have a page that contains a grid
SearchPage.razor
<FiGrid @ref="Grid"
<Columns>
@Columns
</Columns>
<EditFormTemplate Context="contextEntity">
@EditFormTemplate(contextEntity)
</EditFormTemplate>
</FiGrid>
@code{
protected RenderFragment Columns { get; set; }
protected RenderFragment<object> EditFormTemplate { get; set; }
}
this page has a RenderFragment to render the columns and one to render an editForm whenever is necessary
i inherit from this SearchPage to customize this 2 props:
CustomSearchPage.razor
@inherits SearchPage
@{
base.BuildRenderTree(__builder);
}
@code {
private static RenderFragment _customColumns = __builder =>
{
<CustomColumn FieldName="@nameof(Factory.Id)" Width="75px"/>
<CustomColumn FieldName="@nameof(Factory.BusinessName)"/>
<CustomColumn FieldName="@nameof(Factory.Address)"/>
};
private static RenderFragment<object> _customEditFormTemplate = editModel => __builder =>
{
<CustomEditForm Entity="editModel"/>;
};
protected override void OnInitialized()
{
Columns = _customColumns;
EditFormTemplate = _customEditFormTemplate;
base.OnInitialized();
}
}
but it gives me this error on the console:
Operation is not valid due to the current state of the object.
maybe I'm using the wrong approach, in that case, how should I accomplish the same thing?
I need to be able to inherit from a page and pass the page a custom edit form that takes the context of the template
basically I want to pass the context of the template down to a RenderFragment created via code
You are getting the error because you can't build component inheritance in the way you envisage. You can't call BuildRenderTree
manually like this:
base.BuildRenderTree(__builder);
because you don't have a reference to the Renderer's RenderTreeBuilder
.
A RenderFragment
is a delegate defined as:
public delegate void RenderFragment(RenderTreeBuilder builder);
It's run by the Renderer
which invokes it and passes in the Renderer's instance of RenderTreeBuilder
.
All Razor pages are pre-compiled into C# classes. So this simple component DisplayDiv.razor
:
<h3>@Header</h3>
@code {
[Parameter] public string Header { get; set; } = "Hello Blazor";
}
gets pre-compiled by the Razor processor to something like this:
public class DisplayDiv : ComponentBase
{
[Parameter] public string Header { get; set; } = "Hello Blazor";
protected override void BuildRenderTree(RenderTreeBuilder builder)
{
builder.OpenElement(0, "h3");
builder.AddContent(1, this.Header);
builder.CloseElement();
}
}
BuildRenderTree
gets added to ComponentBase
's base RenderFragment which StateHasChanged
passes to the Renderer when it's called to render the component.
private readonly RenderFragment _renderFragment;
public ComponentBase()
{
_renderFragment = builder =>
{
_hasPendingQueuedRender = false;
_hasNeverRendered = false;
BuildRenderTree(builder);
};
}
You are trying to define multiple copies of BuildRenderTree
, which you can't do within the razor page setting. The child BuildRenderTree
method built by the Razor pre-compiler overrides that of the parent.
Your SearchPage
is a component which you then use in a page
<SearchPage>
<Columns>
<CustomColumn FieldName="@nameof(Factory.Id)" Width="75px"/>
<CustomColumn FieldName="@nameof(Factory.BusinessName)"/>
<CustomColumn FieldName="@nameof(Factory.Address)"/>
</Columns>
<EditFormTemplate>
<CustomEditForm Entity="editModel"/>
</EditFormTemplate>
</SearchPage>
You can do inheritance in components, but your parent components need to be classes, not Razor components, and build out any markup directly in code.