Eric Lowe
Eric Lowe

Reputation: 3

How to iterate through child components in Blazor

I'm attempting to have a parent component employ a foreach to iterate through child components in Blazor. For example, I have a Button component and want to be able to nest that in a ButtonGroup component that adds padding around each Button. My code returns a "The type arguments for method...cannot be inferred from the usage. Try specifying the type arguments explicitly." Not sure how to accomplish. Any help appreciated.

Index.razor

<ButtonGroup>
  <Button>Howdy</Button>
  <Button>Doody</Button>
 </ButtonGroup>

Button.razor

<a href="@Url" class="btn btn-primary">@ChildContent</a>

@code {
  [Parameter] public RenderFragment ChildContent { get; set; }
  [Parameter] public string Url { get; set; }

ButtonGroup.razor

@typeparam TItem

<div class="flex flex-wrap py-16 mx-n4">
  @foreach (var item in Items)
  {
  <div class="px-4 d-ib">
    @ChildContent(item)
  </div>
  }
</div>

@code {
  [Parameter] public RenderFragment<TItem> ChildContent { get; set; }
  [Parameter] public IReadOnlyList<TItem> Items { get; set; }
}

Upvotes: 0

Views: 4511

Answers (1)

Nik FP
Nik FP

Reputation: 3073

It appears that you need to approach using ChildContent a bit differently than you have it. By convention with Blazor, ChildContent is used to dictate where to insert the markup that is placed between the opening and closing tags of your component, in circumstances where you have only one section that needs a fragment. That means that your Button.razor component is written correctly.

Things are going sideways when you get into your button group however, and there are a couple of approaches you can take. Templated components will get you there, but this can be much simpler if you use nested components with ChildContent to get what you need.

Starting with your ButtonGroup.razor, you can simplify so the button group just takes child content and places it, and in your case will be a simple wrapper so the CSS does what it needs to do:

<div class="flex flex-wrap py-16 mx-n4">
    @ChildContent
</div>

@code {
 
  [Parameter] public RenderFragment ChildContent { get; set; }
  
}

Note that since this is a simple wrapper, you can remove the generic TItem and just use this as a layout tool.

Then, step up the tree one level, and here is where you iterate your list, and you should be specific with your components nesting at this level. Note the nesting of Button and ButtonGroup, and I assume the type you use for ItemType in your list has properties for Url and ButtonName, but you get the point and you can constrain where needed.

<ButtonGroup>
    @foreach (var item in Items ) {
        <div class="px-4 d-ib">
            <Button Url="@item.Url">@item.ButtonName</Button>
        </div>
</ButtonGroup>

@code {

    [Parameter] public IReadOnlyList<ItemType> Items { get; set; }

}

This way, whatever is nested between the tags of each component has a place in the markup that is generated, so your ButtonGroup surrounds all your Button components and they in turn render the button names, but everything is still being dictated and controlled at the top level.

You could of course do the same thing with Templated Components and there are times they are necessary, but for your use case they would require roughly the same or more code at the top level and be way overkill for what you are trying to do.

Upvotes: 1

Related Questions