New to Telerik UI for Blazor? Download free 30-day trial

Customize the Delete Confirmation Dialog

Environment

Product Grid for Blazor
TreeList for Blazor
Scheduler for Blazor

Description

This KB article answers the following questions:

  • How to customize the built-in Delete Confirmation Dialog of the Grid?
  • I want to add some item details to the text of the Delete Confirmation Dialog to notify the user for the item they are trying to delete. How to achieve that?
  • How to change the text of the buttons in the Delete Confirmation Dialog?
  • How to change the title and the content of the Delete Confirmation Dialog?

Solution

The built-in Delete Confirmation Dialog requires minimum effort and code. It only needs the ConfirmDelete parameter of the Grid to be true. Thus, the dialog itself does not provide customization options. However, there are several ways to achieve a custom Delete Confirmation Dialog.

This article lists several samples for the Grid component. The suggested solutions, however, are applicable for the Treelist and Scheduler components, too.

You can use:

  • Localization - this approach is useful if you just want to change the text of the built-in Delete Confirmation Dialog elements. It does not allow adding item details to the dialog text.
  • Predefined Dialog Component - this option is useful if you want to just change the dialog text and include some details for the item the user tries to delete (for example, record name).
  • Dialog Component - this solution allows you to fully customize the rendering and appearance of the dialog. You may add any desired content there, be that custom text, HTML elements or other components.

Localization

You can enable Localization in your application and provide the desired custom text for the corresponding element keys.

The keys for the elements of the built-in Delete Confirmation Dialog are:

Element Key
Title Grid_ConfirmDeleteTitle
Text Grid_ConfirmDeleteText
OK button Grid_ConfirmDeleteOk
Cancel button Grid_ConfirmDeleteCancel

The keys for the Treelist and Scheduler components are the same, just change the component name before the _ symbol.

Predefined Dialog Component

Use a Predefined Confirm Dialog with the desired custom text. Additionally, you may get the details for the current item and add them to the text:

  • Use the Grid Class parameter to set a z-index style, which is lower than the default Dialog z-index of 10,000.
  • Handle the OnDelete event of the Grid.
  • Display the predefined Dialog in the OnDelete handler.
  • Cancel the event or proceed with the OnDelete logic depending on the user choice.
  • The same approach is applicable to the OnCreate and OnUpdate events.
@using System.ComponentModel.DataAnnotations

<TelerikGrid Data="@GridData"
             Class="z-index-1"
             EditMode="@GridEditMode.Inline"
             OnDelete="@OnGridDelete"
             OnUpdate="@OnGridUpdate"
             Pageable="true"
             PageSize="20"
             Height="90vh">
    <GridColumns>
        <GridColumn Field="@nameof(Product.Name)" />
        <GridColumn Field="@nameof(Product.Price)" DisplayFormat="{0:C2}" />
        <GridColumn Field="@nameof(Product.Quantity)" />
        <GridCommandColumn>
            <GridCommandButton Command="Edit">Edit</GridCommandButton>
            <GridCommandButton Command="Save" ShowInEdit="true">Save</GridCommandButton>
            <GridCommandButton Command="Cancel" ShowInEdit="true">Cancel</GridCommandButton>
            <GridCommandButton Command="Delete">Delete</GridCommandButton>
        </GridCommandColumn>
    </GridColumns>
</TelerikGrid>

<style>
    /* Create a stacking context for the whole Grid,
       which is lower than the stacking context of the Dialog. */
    .z-index-1 {
        z-index: 1;
    }
</style>

@code {
    [CascadingParameter]
    public DialogFactory? TelerikDialogs { get; set; }

    private List<Product> GridData { get; set; } = new();

    private int LastId { get; set; }

    private async Task OnGridUpdate(GridCommandEventArgs args)
    {
        var updatedItem = (Product)args.Item;

        bool cancelled = await ShouldCancel("Updating", updatedItem.Name);
        if (cancelled)
        {
            args.IsCancelled = true;
            return;
        }

        var originalItemIndex = GridData.FindIndex(i => i.Id == updatedItem.Id);

        if (originalItemIndex != -1)
        {
            GridData[originalItemIndex] = updatedItem;
        }
    }

    private async Task OnGridDelete(GridCommandEventArgs args)
    {
        var deletedItem = (Product)args.Item;

        bool cancelled = await ShouldCancel("Deleting", deletedItem.Name);
        if (cancelled)
        {
            args.IsCancelled = true;
            return;
        }

        GridData.Remove(deletedItem);
    }

    private async Task<bool> ShouldCancel(string operation, string name)
    {
        string dialogMessage = $"{operation} product {name}. Continue?";

        bool dialogResult = true;

        if (TelerikDialogs != null)
        {
            dialogResult = await TelerikDialogs.ConfirmAsync(dialogMessage, "Confirm Data Change");
        }

        return !dialogResult;
    }

    protected override void OnInitialized()
    {
        for (int i = 1; i <= 30; i++)
        {
            GridData.Add(new Product()
            {
                Id = ++LastId,
                Name = $"Name {LastId}",
                Price = Random.Shared.Next(1, 100) * 1.23m,
                Quantity = Random.Shared.Next(0, 1000),
            });
        }
    }

    public class Product
    {
        public int Id { get; set; }
        [Required]
        public string Name { get; set; } = "Default Name";
        public decimal Price { get; set; }
        public int Quantity { get; set; }
    }
}

Dialog Component

Using the Dialog component will let you have fully customized Delete Confirmation Dialog. To handle the scenario:

  • Declare a Dialog instance and add the desired content and buttons there. Normally, you would need at least two buttons - for confirmation and cancelling the delete operation.
  • Use custom commands instead of the built-in Save and Delete commands to obtain the data item and show the Dialog component.
  • Handle the Dialog button clicks:
  • The same approach is applicable to the OnCreate and OnUpdate events.
@using System.ComponentModel.DataAnnotations

<TelerikGrid @ref="@GridRef"
             Data="@GridData"
             EditMode="@GridEditMode.Inline"
             OnCancel="@OnGridCancel"
             Pageable="true"
             PageSize="20"
             Height="90vh">
    <GridColumns>
        <GridColumn Field="@nameof(Product.Name)" />
        <GridColumn Field="@nameof(Product.Price)" DisplayFormat="{0:C2}" />
        <GridColumn Field="@nameof(Product.Quantity)" />
        <GridCommandColumn>
            <GridCommandButton Command="Edit">Edit</GridCommandButton>
            <GridCommandButton Command="CustomSave"
                               OnClick="@OnGridSave"
                               ShowInEdit="true">Save</GridCommandButton>
            <GridCommandButton Command="Cancel" ShowInEdit="true">Cancel</GridCommandButton>
            <GridCommandButton Command="CustomDelete"
                               OnClick="@OnGridDelete">Delete</GridCommandButton>
        </GridCommandColumn>
    </GridColumns>
</TelerikGrid>

<TelerikDialog @bind-Visible="@DialogVisible"
               Width="300px"
               ButtonsLayout="@DialogButtonsLayout.End">
    <DialogTitle>
        Confirm Data Change
    </DialogTitle>
    <DialogContent>
        @DialogContent
    </DialogContent>
    <DialogButtons>
        <TelerikButton OnClick="@( () => OnDialogButtonClick(true) )"
                       ThemeColor="@ThemeConstants.Button.ThemeColor.Success">OK</TelerikButton>
        <TelerikButton OnClick="@( () => OnDialogButtonClick(false) )"
                       ThemeColor="@ThemeConstants.Button.ThemeColor.Error">Cancel</TelerikButton>
    </DialogButtons>
</TelerikDialog>

@code {
    private TelerikGrid<Product>? GridRef { get; set; }
    private List<Product> GridData { get; set; } = new();

    private bool DialogVisible { get; set; }

    private Product? ItemToUpdate { get; set; }
    private Product? ItemToDelete { get; set; }

    private MarkupString DialogContent { get; set; } = new();

    private int LastId { get; set; }

    private void OnGridSave(GridCommandEventArgs args)
    {
        var itemToUpdate = (Product)args.Item;
        ItemToUpdate = itemToUpdate;

        DialogContent = new MarkupString($"<p>Saving product <strong>{ItemToUpdate.Name}</strong>. <br /> Do you want to continue?</p>");
        DialogVisible = true;
    }

    private void OnGridDelete(GridCommandEventArgs args)
    {
        var deletedItem = (Product)args.Item;
        ItemToDelete = deletedItem;

        DialogContent = new MarkupString($"<p>Deleting product <strong>{ItemToDelete.Name}</strong>. <br /> Do you want to continue?</p>");
        DialogVisible = true;
    }

    private void OnGridCancel(GridCommandEventArgs args)
    {
        ItemToUpdate = null;
    }

    private async Task OnDialogButtonClick(bool operationConfirmed)
    {
        if (operationConfirmed)
        {
            if (ItemToDelete != null)
            {
                GridData.Remove(ItemToDelete);
            }

            if (ItemToUpdate != null)
            {
                var originalItemIndex = GridData.FindIndex(i => i.Id == ItemToUpdate.Id);

                if (originalItemIndex != -1)
                {
                    GridData[originalItemIndex] = ItemToUpdate;
                }
            }

            GridRef!.Rebind();
        }
        else if (ItemToUpdate != null)
        {
            var gridState = GridRef!.GetState();

            gridState.EditItem = null!;
            gridState.OriginalEditItem = null!;

            await GridRef!.SetStateAsync(gridState);
        }

        DialogVisible = false;

        ItemToDelete = null;
        ItemToUpdate = null;
    }

    protected override void OnInitialized()
    {
        for (int i = 1; i <= 30; i++)
        {
            GridData.Add(new Product()
            {
                Id = ++LastId,
                Name = $"Name {LastId}",
                Price = Random.Shared.Next(1, 100) * 1.23m,
                Quantity = Random.Shared.Next(0, 1000),
            });
        }
    }

    public class Product
    {
        public int Id { get; set; }
        [Required]
        public string Name { get; set; } = "Default Name";
        public decimal Price { get; set; }
        public int Quantity { get; set; }
    }
}

See Also

In this article