Home > OS >  Possible to access a property out of context in blazor template?
Possible to access a property out of context in blazor template?

Time:11-21

The code below allows me to display a grid with items I pass to as a list of Titem of a generic type. Internally the Grid component creates a collection of rows (Rows) of type GridRow which also hold the generic Item for each row.

Index.razor

<Grid Items="Transactions" @ref="MyGrid">
    <GridBody Context="transaction">
        <GridCell>@transaction.Date</GridCell>
        <GridCell>
        </GridCell>
    </GridBody>
</Grid>    

Grid.razor

@typeparam TItem
@attribute [CascadingTypeParameter(nameof(TItem))];

<table>
    <tbody>
        @foreach (var row in Rows)
        {
            <tr>
                @GridBody(row.Item)
            </tr>
        }
    </tbody>
</table>

Grid.razor.cs

public class GridRow<TItem>
{
    public TItem Item { get; set; } = default!;
    public int Id { get; set; } = default!;
}


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

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

    public List<GridRow<TItem>> Rows { get; set; } = new();

    protected override void OnInitialized()
    {
        int i = 1;
        foreach (var item in Items)
        {
            var gridRow = new GridRow<TItem>()
            {
                Id = i,
                Item = item,
            };
            Rows.Add(gridRow);
            i  ;
        }
    }
}

All works well, but I wish to access the row.Id in index.razor, but I can't do this because the content provided is of type TItem. I would like to the following:

<Grid Items="Transactions" @ref="MyGrid">
    <GridBody Context="transaction">
        <GridCell>@transaction.Date</GridCell>
        <GridCell>
            @row.Id     // .. this is what I need
        </GridCell>
    </GridBody>
</Grid>   

Is the above possible while also preserving the use the transaction context? The Id cannot come from the transaction object as the Grid component should know nothing about the type of objects it is rendering and therefore not rely on an ID in the transaction object.

CodePudding user response:

One option i see is to use Transactions.IndexOf(transaction) 1 Then you dont need to use the grid id.

A more proper way could be to create a method on the Grid model and then get the id by passing the current TItem.

CodePudding user response:

If you want Titem to have an Id property then you need to define an interface that implements what you want.

public interface IWithIntId
{
    public int Id {get;}
} 

And then:

public class GridRow<TItem> : 
     where TItem : class, IWithIntId
{
    public TItem Item { get; set; } = default!;
    public int Id { get; set; } = default!;
}

You will then be able to access the Id property in any GridRow.

CodePudding user response:

I chose to rework my code to use the row as the context rather than Genric type itself, below is the code that worked for me:

Index.razor

<Grid Items="Transactions" @ref="MyGrid">
    <GridBody Context="row">
        <GridCell>@row.Item.Date</GridCell>
        <GridCell>
            @row.Id
        </GridCell>
    </GridBody>
</Grid> 

Grid.razor

@typeparam TItem @attribute [CascadingTypeParameter(nameof(TItem))];

<table>
    <tbody>
        @foreach (var row in Rows)
        {
            <tr>
                @GridBody(row)
            </tr>
        }
    </tbody>
</table>

Grid.razor.cs

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!;

    public List<GridRow<TItem>> Rows { get; set; } = new();
}
  • Related