blazor.net-8.0mudblazor

.Net 8 MudBlazor MudTable. Custom Inline Edit not working


I am trying out Blazor with the MudBlazor Component Library. I played around with their MudTable with built in editing. I'd like to try to get my own custom editing going just so that I can then add my delete button and have it styled and spaced in the same way as the Edit button. To Create a clickable custom Edit button, it's easy to create a MudIcon with a click event. This can then point to a method where you call

table.SetEditingItem(row);

However, no matter what I try, I cannot get this method to work. Clicking the icon does not trigged the selected row into edit mode. Please note that I have tried multiple things in the sample code I include below. So you're seeing a couple of hours of googling, editing, trying, failing etc. This code should work completely standalone with no infrastructure dependencies if anyone wants to give it a try.

@page "/test/index"
@rendermode InteractiveServer
@attribute [StreamRendering]
@inject ILocationService _locationService
@inject IAppLogger _logger
@inject IHttpContextAccessor _context;
@inject ISnackbar Snackbar

<MudTable @ref=@table
          ReadOnly="true"
          T="Element"
          Items="@elements"
          Hover="true"
          SortLabel="Sort By"
          OnCommitEditClick="@(() => Snackbar.Add("Commit Edit Handler Invoked"))"
          RowEditPreview="BackupItem"
          RowEditCancel="ResetItemToOriginalValues"
          CommitEditIcon=""
          SelectedItemChanged="OnSelectedItemChanged"
          SelectedItem="elementBeforeEdit"
          bind-SelectedItem="elementBeforeEdit"
          EditTrigger="TableEditTrigger.EditButton"
          >
    <ColGroup>
        <col />
        <col style="width:20%;" />
        <col style="width:50%;" />
        <col style="width:30%" />
    </ColGroup>
    <HeaderContent>
        <MudTh></MudTh>
        <MudTh><MudTableSortLabel SortBy="new Func<Element, object>(x=>x.Number)">Nr</MudTableSortLabel></MudTh>
        <MudTh><MudTableSortLabel SortBy="new Func<Element, object>(x=>x.Name)">Name</MudTableSortLabel></MudTh>
        <MudTh><MudTableSortLabel InitialDirection="SortDirection.Ascending" SortBy="new Func<Element, object>(x=>x.Age)">Age</MudTableSortLabel></MudTh>
    </HeaderContent>
    <RowTemplate>
        <MudTd></MudTd>
        <MudTd DataLabel="Nr">@context.Number</MudTd>
        <MudTd DataLabel="Name">@context.Name</MudTd>
        <MudTd DataLabel="Age">@context.Age</MudTd>
        <MudTd>
            <MudTooltip Text="Edit">
                <MudIconButton Size="Size.Small" Icon="@Icons.Material.Filled.Edit" OnClick="() => OnEditSelected(table.SelectedItem)"></MudIconButton>
            </MudTooltip>
        </MudTd>
    </RowTemplate>
    <RowEditingTemplate>
        <MudTd>
            <div style="display: flex; gap: 10px;">
                <MudTooltip Text="Commit Edit">
                    <MudIconButton Size="Size.Small" Icon="@Icons.Material.Filled.Check" OnClick="OnCommitEditButtonClicked"></MudIconButton>
                </MudTooltip>
                <MudTooltip Text="Cancel Edit">
                    <MudIconButton Size="Size.Small" Icon="@Icons.Material.Filled.Cancel" OnClick="OnCancelEditButtonClicked"></MudIconButton>
                </MudTooltip>
            </div>
        </MudTd>
        <MudTd DataLabel="Nr">@context.Number</MudTd>
        <MudTd DataLabel="Name">
            <MudTextField @bind-Value="@context.Name" Required />
        </MudTd>
        <MudTd DataLabel="Age">
            <MudNumericField @bind-Value="@context.Age" Required />
        </MudTd>
    </RowEditingTemplate>
    <PagerContent>
        <MudTablePager />
    </PagerContent>
</MudTable>


@code {
    private MudTable<Element> table;
    private Element elementBeforeEdit;
    private IEnumerable<Element> elements = new List<Element>();

    protected override void OnInitialized()
    {
        elements = GetData();
    }


    protected override void OnAfterRender(bool firstRender)
    {
        if (firstRender)
        {
            elementBeforeEdit = elements.First();
            StateHasChanged();
        }
    }

    private void OnCommitEditButtonClicked(MouseEventArgs e)
    {
        table.RowEditCommit?.Invoke(table.SelectedItem);
        table.OnCommitEditClick.InvokeAsync(e);
        table.SetSelectedItem(null);
    }

    private void OnCancelEditButtonClicked(MouseEventArgs e)
    {
        table.RowEditCancel?.Invoke(table.SelectedItem);
        table.OnCancelEditClick.InvokeAsync(e);
        table.SetSelectedItem(null);
    }


    private void OnSelectedItemChanged(Element element)
    {
        elementBeforeEdit = element;
        table.SetEditingItem(element);
    }


    private async Task OnEditSelected(Element element)
    {
        await Task.Delay(150);
        table.ReadOnly = false;
        table.SetSelectedItem(element);
        table.SetEditingItem(null);
        await Task.Delay(150);
        table.SetEditingItem(elementBeforeEdit);
        StateHasChanged();
    }


    private void BackupItem(object element)
    {
        elementBeforeEdit = new()
            {
                Name = ((Element)element).Name,
                Age = ((Element)element).Age,
            };
    }

    private void ResetItemToOriginalValues(object element)
    {
        ((Element)element).Name = elementBeforeEdit.Name;
        ((Element)element).Age = elementBeforeEdit.Age;
    }

    private IEnumerable<Element> GetData()
    {
        return new List<Element>()
        {
            new() { Number = 1, Name = "Peter Parker", Age = 16 },
            new() { Number = 2, Name = "Bruce Wayne", Age = 31 },
            new() { Number = 3, Name = "Clark Kent", Age = 31 },
            new() { Number = 4, Name = "Barry Allen", Age = 29 },
            new() { Number = 5, Name = "Tony Stark", Age = 52 },
        };
    }

    class Element
    {
        public int Number { get; set; }
        public string Name { get; set; }
        public int Age { get; set; }
    }
}

I've played around with setting table ReadOnly. With the table, I've played around with EditTrigger, SelectedItem, bind-SelectedItem etc. In the OnEditSelected method, I've played around with nulling, Task delays etc. These were all suggested by people on Stack, GitHub etc. Nothing seems to work. Any help greatly appreciated.


Solution

  • You can use EditButtonContent which is a RenderFragment<EditButtonContext>. GitHub class

    From the docs

    Defines the edit button that will be rendered when EditTrigger.EditButton

    Here anything you place within EditButtonContent will be rendered in place of the default edit button. So you can place your delete icon buttons within it aswell.

    <EditButtonContent Context="button">
            <MudIconButton Size="@Size.Small" Icon="@Icons.Material.Outlined.Edit" Class="pa-0" OnClick="()=>OnEditSelected(button)" Disabled="@button.ButtonDisabled" />
            <MudIconButton Size="Size.Small" Icon="@Icons.Material.Filled.Cancel" OnClick="() => OnDeleteSelected(button.Item)"></MudIconButton>
    </EditButtonContent>
    

    EditButtonContent has the Context property which gives context of the row that is clicked by passing it as an object?.

    Note: MudTable.Readonly needs to be false in order for the buttons to be visible.

    In the handler methods make use of the context to trigger the built-in editing Action MudBlazorFix.EditButtonContext.ButtonAction instead of calling the MudTable methods.

    private void OnDeleteSelected(object untypedElement)
    {
        Element element = (Element)untypedElement;
        Snackbar.Add($"item to delete: {element.Name}", Severity.Error);
        Elements = Elements.Where(x=>x.Number != element.Number);
    }
    
    private async Task OnEditSelected(MudBlazorFix.EditButtonContext button)
    {
        Element element = (Element)button.Item;
        Snackbar.Add($"item to edit: {element.Name}", Severity.Info);
        button.ButtonAction();
    }
    

    You can make use of the following properties to change the built-in icons.

    CommitEditIcon="@Icons.Material.Filled.Save"
    CancelEditIcon="@Icons.Material.Filled.EditOff"
    

    Demo 👉 MudBlazor Snippet