Pass row data down to child cell component in Grid without passing as a parameter

356 Views Asked by At

I have built a simple Grid component which mostly works, however I have run into a situation where I need to pass down the instance of GridRow being rendered to the GridCell to allow the GridCell component to access it's properties and modify if necessary.

The component is constructed from 3 parts, Grid, GridBody and GridCell as follows:

Usage in index.razor

<Grid Items="MyItems">
    <GridBody Context="row">
        <GridCell>@row.Id</GridCell>
        <GridCell>@row.Item.Description</GridCell>
    </GridBody>
</Grid>

Grid.razor:

@namespace Accounting.Web.Components
@typeparam TItem
@attribute [CascadingTypeParameter(nameof(TItem))];

<CascadingValue Value="this" IsFixed="true">
    <table cellspacing="0">
        <tbody>
            @foreach (var row in Rows)
            {
                <tr>
                    @GridBody(row)
                </tr>
            }
        </tbody>
    </table>
</CascadingValue>

Grid.razor.cs

namespace Accounting.Web.Components
{
    public class GridRow<TItem>
    {
        public TItem Item { get; set; } = default!;
        public int Id { get; set; } = default!;
    }

    public partial class Grid<TItem>
    {
        [Parameter]
        public RenderFragment<GridRow<TItem>> GridBody { get; set; } = default!;

        [Parameter]
        public IList<TItem> Items { get; set; } = default!;
    }
}

GridBody.razor

@namespace Accounting.Web.Components
@typeparam GridRow

GridBody.razor.cs
using Microsoft.AspNetCore.Components;

namespace Accounting.Web.Components
{
    public partial class GridBody<GridRow>
    {
        [Parameter]
        public RenderFragment<GridRow> ChildContent { get; set; }
    }
}

GridCell.razor

@namespace Accounting.Web.Components
@typeparam TItem

<td>
    @ChildContent
</td>

GridCell.razor.cs

using Microsoft.AspNetCore.Components;

namespace Accounting.Web.Components
{
    public partial class GridCell<TItem>
    {
        [CascadingParameter]
        public Grid<TItem> Grid { get; set; } = default!;

        [Parameter]
        public int Id { get; set; } = default!;

        [Parameter]
        public GridRow<TItem> Row { get; set; } = default!;

        [Parameter]
        public RenderFragment ChildContent { get; set; } = default!;
    }
}

In my various attempts I have attempted to pass down the row from GridBody to the GridCell but nothing has come close to working. The only way I can accomplish what I need is to directly pass row as a parameter in the tag in index.razor as follows:

<GridCell Row=row>@row.Id</GridCell>

This means I would ned to pass it in every GridCell. Is there a more elegant of passing the row down to the GridCell above without the use of a parameter?

1

There are 1 best solutions below

3
Musaffar Patel On

The solution was straight forward, simply use a cascading value component to the Grid.razor:

Grid.razor:

@namespace Accounting.Web.Components
@typeparam TItem
@attribute [CascadingTypeParameter(nameof(TItem))];

<CascadingValue Value="this" IsFixed="true">
    <table cellspacing="0">
        <tbody>
            @foreach (var row in Rows)
            {
                <tr>
                    <CascadingValue Value="row">
                        @GridBody(row)
                    </CascadingValue>
                </tr>
            }
        </tbody>
    </table>
</CascadingValue>

GridCell.razor

@namespace Accounting.Web.Components
@typeparam TItem

<td>
    @ChildContent
</td>

GridCell.razor.cs

using Microsoft.AspNetCore.Components;

namespace Accounting.Web.Components
{
    public partial class GridCell<TItem>
    {
        [CascadingParameter]
        public Grid<TItem> Grid { get; set; } = default!;

        [Parameter]
        public int Id { get; set; } = default!;

        [CascadingParameter]
        public GridRow<TItem> Row { get; set; } = default!;

        [Parameter]
        public RenderFragment ChildContent { get; set; } = default!;
    }
}