Home > OS >  Conditionally pass renderfragments to components
Conditionally pass renderfragments to components

Time:11-17

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>).

  • Related