I'm trying to conditionally pass renderfragments to a component using @if blocks but I get the build error
Unrecognized child content inside component 'XXX'.
If I have a parent component AwesomeComponent like this:
@code
{
[Parameter] public RenderFragment ItemTemplate {get; set;}
[Parameter] public RenderFragment AnotherItemTemplate {get; set;}
[Parameter] public RenderFragment YetAnotherItemTemplate {get; set;}
}
And then conditionally want to render a renderfragment like this:
<AwesomeComponent>
<ItemTemplate>
// some code here
</ItemTemplate>
<AnotherItemTemplate>
// blabla
</AnotherItemTemplate>
// This one i want to pass conditionally, the intuitive way is like this
@if (condition)
{
<YetAnotherItemTemplate>
// wooho
</YetAnotherItemTemplate>
}
I get the Unrecognized child content inside component
error.
I have seen solutions like this. However I am also having these issues with third party components and therefore I cannot control the visibility like suggested there. Furhtermore I do not believe it's an elegant solution.
Have anyone in similar situation found a way of dealing with conditional renderfragments as described? I find it a very big limitation in Blazor.
CodePudding user response:
You can't do it that way. Your conditional code needs to be in the component. You then pass the condition selector into the component as a Parameter
. See the code below.
AwesomeComponent
<h3>AwesomeComponent</h3>
@switch (FragmentToRender)
{
case 1:
@this.ItemTemplate
break;
case 2:
@this.AnotherItemTemplate
break;
case 3:
@this.YetAnotherItemTemplate
break;
default:
<span>No Component Selected</span>
break;
}
@code
{
[Parameter] public RenderFragment? ItemTemplate {get; set;}
[Parameter] public RenderFragment? AnotherItemTemplate {get; set;}
[Parameter] public RenderFragment? YetAnotherItemTemplate {get; set;}
[Parameter] public int FragmentToRender {get; set;}
}
Demo Page
@page "/"
<h3>Awesome</h3>
<div class="m-2">
<button class="btn btn-primary" @onclick="() => this.ChangeComponent(1)">Display First Fragement</button>
<button class="btn btn-secondary" @onclick="() => this.ChangeComponent(2)">Display Second Fragment</button>
<button class="btn btn-dark" @onclick="() => this.ChangeComponent(3)">Display Third Fragment</button>
</div>
<div class="m-2">
<AwesomeComponent FragmentToRender=this.componentToDisplay>
<ItemTemplate>
One
</ItemTemplate>
<AnotherItemTemplate>
Two
</AnotherItemTemplate>
<YetAnotherItemTemplate>
Three
</YetAnotherItemTemplate>
</AwesomeComponent>
</div>
@code {
private int componentToDisplay;
private void ChangeComponent(int value)
=> this.componentToDisplay = value;
}
CodePudding user response:
Below is a code snippet demonstrating how to render a parameter RenderFragment delegate Footer conditionally. Copy and test, and then try to adapt its usage to your code:
ChildComponent.razor
<div>
@if (Body != null)
{
@Body
}
@if (Footer != null)
{
@Footer
}
</div>
@code
{
[Parameter]
public RenderFragment Body { get; set; }
[Parameter]
public RenderFragment Footer { get; set; }
}
ParentComponent.razor
<ChildComponent>
<Body>
<p>@Text</p>
</Body>
<Footer>
@ParentFooter
</Footer>
</ChildComponent>
@code
{
[Parameter]
public string Text { get; set; }
[Parameter]
public RenderFragment ParentFooter { get; set; }
}
Index.razor
@page "/"
@*Used without Footer*@
@*<ParentComponent Text="Hello World"></ParentComponent>*@
@*Used with Footer*@
<ParentComponent ParentFooter="Footer" Text="Hello World">
</ParentComponent>
@code{
private RenderFragment Footer =@<p>This is the Footer</p>;
}
CodePudding user response:
As already mentioned, the @if
has to be inside <AwesomeComponent>
.
The reason is that the rules for Templates are diffferent from those for content.
But
... and therefore I cannot control the visibility like suggested there
In that case the brute force solution is to move the @if
outside the component:
@if (condition)
{
<AwesomeComponent >
// templates _including_ YetAnotherItemTemplate
</AwesomeComponent >
}
else
{
<AwesomeComponent>
// templates _without_ YetAnotherItemTemplate
</AwesomeComponent >
}
Not pretty but you could package this in yet another Component (<AwesomeComponentWrapper>
).