Aspnetcore: [Blazor] Allow implicit ChildContent for template components

Created on 12 Feb 2019  路  2Comments  路  Source: dotnet/aspnetcore

Right now it's not possible to have a template component and use it along with ChildContent. For example if I have i component:

MyTemplatedComponent.cshtml

<div class="class1">
    <div class="class2">something here</div>
    @ChildContent
    <div class="class3">
        @Feedback
    </div>    
</div>
@functions{
    [Parameter] protected RenderFragment ChildContent { get; set; }
    [Parameter] protected RenderFragment Feedback { get; set; }
}

Wanted usage:

<MyTemplatedComponent>
    This should be a component default content!!
    <Feedback>
        Hello from feedback!
    </Feedback>
<MyTemplatedComponent>

The problem is that I cannot use it like in the previous example because I get an error:

Unrecognized child content inside component 'MyTemplatedComponent'. The component 'MyTemplatedComponent' accepts child content through the following top-level items: 'ChildContent', 'Feedback'.

Current usage:
If I specifiy ChildContent explicitly it works

<MyTemplatedComponent>
    <ChildContent>
        This should be a component default content!!
    </ChildContent>
    <Feedback>
        Hello from feedback!
    </Feedback>
<MyTemplatedComponent>

I think that template parameters should behave as a placeholder and be used when specified. Also ChildContent content should be used implicitly in all cases. If it's not specified it should be a default content for a component.

area-blazor

Most helpful comment

FWIW, I agree with OP that the proposed syntax should be supported. I also do not understand the comment by @mkArtakMSFT - what is the possibility being eliminated here?

In my case I have this component:

@if (Template != null)
{
  @Template(ChildContent)
}
else
{
  <h4 class="card-title">@ChildContent</h4>
}

@functions {
    [Parameter]
    RenderFragment ChildContent { get; set; }

    [Parameter]
    RenderFragment<RenderFragment> Template { get; set; }
}

I can now use this component like this (without a template):

<CardTitle>This is the card title</CardTitle>

I can also do this for a more complex title:

<CardTitle><img src="..." /> This is the card title</CardTitle>

However, when specifying a template, I suddenly need to do this:

<CardTitle>
  <Template>
    <h2 class="card-title">@context</h2>
  <Template>
  <ChildContent>
    <img src="..." /> This is the card title
  </ChildContent>
</CardTitle>

This is bad! I don't see why the content in ChildContent could not automatically be pulled into that RenderFragment if it is found without a containing block. I.e.:

<CardTitle>
  <Template>
    <h2 class="card-title">@context</h2>
  <Template>

  <img src="..." /> This is the card title
</CardTitle>

And here's an additional thought. If you insist on using top-level containing blocks for this use case, it is still bad that the block now MUST be named ChildContent. If I have to use top-level blocks, I would rather call this block Content or something else altogether - but if I do that, the block needs to be specified at all times, even when no templates are included.

I believe you should reconsider your design in this area. The current system does not allow for the most convenient public-facing (i.e. component user) APIs to be constructed - or at least it makes it quite a burden on the component developer to add support for various common structural variations.

All 2 comments

Thanks for contacting us, @stsrki.
Your proposal would eliminate the possibility of having a content, which contains other components.
Hence, the current working mechanism of specifying each content explicitly is the intended way to do this.

/cc @danroth27

FWIW, I agree with OP that the proposed syntax should be supported. I also do not understand the comment by @mkArtakMSFT - what is the possibility being eliminated here?

In my case I have this component:

@if (Template != null)
{
  @Template(ChildContent)
}
else
{
  <h4 class="card-title">@ChildContent</h4>
}

@functions {
    [Parameter]
    RenderFragment ChildContent { get; set; }

    [Parameter]
    RenderFragment<RenderFragment> Template { get; set; }
}

I can now use this component like this (without a template):

<CardTitle>This is the card title</CardTitle>

I can also do this for a more complex title:

<CardTitle><img src="..." /> This is the card title</CardTitle>

However, when specifying a template, I suddenly need to do this:

<CardTitle>
  <Template>
    <h2 class="card-title">@context</h2>
  <Template>
  <ChildContent>
    <img src="..." /> This is the card title
  </ChildContent>
</CardTitle>

This is bad! I don't see why the content in ChildContent could not automatically be pulled into that RenderFragment if it is found without a containing block. I.e.:

<CardTitle>
  <Template>
    <h2 class="card-title">@context</h2>
  <Template>

  <img src="..." /> This is the card title
</CardTitle>

And here's an additional thought. If you insist on using top-level containing blocks for this use case, it is still bad that the block now MUST be named ChildContent. If I have to use top-level blocks, I would rather call this block Content or something else altogether - but if I do that, the block needs to be specified at all times, even when no templates are included.

I believe you should reconsider your design in this area. The current system does not allow for the most convenient public-facing (i.e. component user) APIs to be constructed - or at least it makes it quite a burden on the component developer to add support for various common structural variations.

Was this page helpful?
0 / 5 - 0 ratings