Skip to content

Commit ed83bba

Browse files
committed
kb(grid): Revamp Custom Delete Confirmation examples
1 parent da56423 commit ed83bba

File tree

1 file changed

+197
-86
lines changed

1 file changed

+197
-86
lines changed

knowledge-base/grid-customize-delete-confirmation-dialog.md

Lines changed: 197 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,19 @@ page_title: Customize the Delete Confirmation Dialogs
66
slug: grid-kb-customize-delete-confirmation-dialog
77
position:
88
tags: grid, customize, delete, confirmation, dialog, message
9-
ticketid: 1553006
9+
ticketid: 1553006, 1660439, 1625888
1010
res_type: kb
1111
---
1212

1313
## Environment
14+
1415
<table>
15-
<tbody>
16-
<tr>
17-
<td>Product</td>
18-
<td>Grid for Blazor <br/> TreeList for Blazor <br/> Scheduler for Blazor </td>
19-
</tr>
20-
</tbody>
16+
<tbody>
17+
<tr>
18+
<td>Product</td>
19+
<td>Grid for Blazor <br/> TreeList for Blazor <br/> Scheduler for Blazor </td>
20+
</tr>
21+
</tbody>
2122
</table>
2223

2324

@@ -63,70 +64,120 @@ The keys for the elements of the built-in Delete Confirmation Dialog are:
6364

6465
Use [Predefined Confirm Dialog]({%slug dialog-predefined%}#confirm) with the desired custom text. Additionally, you may get the details for the current item and add them to the text:
6566

66-
* Handle the [`OnDelete`]({%slug grid-events%}#cud-events) event of the Grid
67-
* Display the Predefined Dialog in the `OnDelete` handler
68-
* Cancel the event or proceed with the `OnDelete` logic depending on the user choice
67+
* Use the Grid `Class` parameter to set a `z-index` style, which is lower than the default Dialog `z-index` of 10,000.
68+
* Handle the [`OnDelete`]({%slug grid-events%}#cud-events) event of the Grid.
69+
* Display the predefined Dialog in the `OnDelete` handler.
70+
* Cancel the event or proceed with the `OnDelete` logic depending on the user choice.
71+
* The same approach is applicable for the `OnCreate` and `OnUpdate` events.
6972

7073
````CSHTML
71-
<TelerikGrid Data=@GridData
74+
@using System.ComponentModel.DataAnnotations
75+
76+
<TelerikGrid Data="@GridData"
77+
Class="z-index-1"
7278
EditMode="@GridEditMode.Inline"
79+
OnDelete="@OnGridDelete"
80+
OnUpdate="@OnGridUpdate"
7381
Pageable="true"
74-
OnDelete="@DeleteHandler">
82+
PageSize="20"
83+
Height="90vh">
7584
<GridColumns>
76-
<GridColumn Field=@nameof(SampleData.ID) Title="ID" Editable="false" />
77-
<GridColumn Field=@nameof(SampleData.Name) Title="Name" />
78-
<GridCommandColumn>
79-
<GridCommandButton Command="Delete" Icon="@SvgIcon.Trash">Delete</GridCommandButton>
85+
<GridColumn Field="@nameof(Product.Name)" />
86+
<GridColumn Field="@nameof(Product.Price)" DisplayFormat="{0:C2}" />
87+
<GridColumn Field="@nameof(Product.Quantity)" />
88+
<GridCommandColumn>
89+
<GridCommandButton Command="Edit">Edit</GridCommandButton>
90+
<GridCommandButton Command="Save" ShowInEdit="true">Save</GridCommandButton>
91+
<GridCommandButton Command="Cancel" ShowInEdit="true">Cancel</GridCommandButton>
92+
<GridCommandButton Command="Delete">Delete</GridCommandButton>
8093
</GridCommandColumn>
8194
</GridColumns>
8295
</TelerikGrid>
8396
84-
@code {
85-
private List<SampleData> GridData { get; set; }
97+
<style>
98+
/* Create a stacking context for the whole Grid,
99+
which is lower than the stacking context of the Dialog. */
100+
.z-index-1 {
101+
z-index: 1;
102+
}
103+
</style>
86104
105+
@code {
87106
[CascadingParameter]
88-
public DialogFactory Dialogs { get; set; }
107+
public DialogFactory? TelerikDialogs { get; set; }
89108
90-
private bool confirmDelete { get; set; }
109+
private List<Product> GridData { get; set; } = new();
91110
92-
private async Task DeleteHandler(GridCommandEventArgs args)
93-
{
94-
SampleData item = (SampleData)args.Item;
111+
private int LastId { get; set; }
95112
96-
//show dialog and use a bool to save its result
97-
confirmDelete = await Dialogs.ConfirmAsync($"Are you sure you want to delete {item.Name}?", "Please confirm!");
113+
private async Task OnGridUpdate(GridCommandEventArgs args)
114+
{
115+
var updatedItem = (Product)args.Item;
98116
99-
//cancel the delete if the user did not confirm
100-
if (!confirmDelete)
117+
bool cancelled = await ShouldCancel("Updating", updatedItem.Name);
118+
if (cancelled)
101119
{
102120
args.IsCancelled = true;
121+
return;
103122
}
104-
//delete the item if the user confirms
105-
else
123+
124+
var originalItemIndex = GridData.FindIndex(i => i.Id == updatedItem.Id);
125+
126+
if (originalItemIndex != -1)
106127
{
107-
GridData.Remove(item);
128+
GridData[originalItemIndex] = updatedItem;
108129
}
109130
}
110131
111-
protected override async Task OnInitializedAsync()
132+
private async Task OnGridDelete(GridCommandEventArgs args)
112133
{
113-
GridData = new List<SampleData>();
134+
var deletedItem = (Product)args.Item;
114135
115-
for (int i = 0; i < 50; i++)
136+
bool cancelled = await ShouldCancel("Deleting", deletedItem.Name);
137+
if (cancelled)
116138
{
117-
GridData.Add(new SampleData()
118-
{
119-
ID = i,
120-
Name = "Name " + i.ToString()
121-
});
139+
args.IsCancelled = true;
140+
return;
122141
}
142+
143+
GridData.Remove(deletedItem);
123144
}
124145
125-
// in a real case, keep the models in dedicated locations, this is just an easy to copy and see example
126-
public class SampleData
146+
private async Task<bool> ShouldCancel(string operation, string name)
127147
{
128-
public int ID { get; set; }
129-
public string Name { get; set; }
148+
string dialogMessage = $"{operation} product {name}. Continue?";
149+
150+
bool dialogResult = true;
151+
152+
if (TelerikDialogs != null)
153+
{
154+
dialogResult = await TelerikDialogs.ConfirmAsync(dialogMessage, "Confirm Data Change");
155+
}
156+
157+
return !dialogResult;
158+
}
159+
160+
protected override void OnInitialized()
161+
{
162+
for (int i = 1; i <= 30; i++)
163+
{
164+
GridData.Add(new Product()
165+
{
166+
Id = ++LastId,
167+
Name = $"Name {LastId}",
168+
Price = Random.Shared.Next(1, 100) * 1.23m,
169+
Quantity = Random.Shared.Next(0, 1000),
170+
});
171+
}
172+
}
173+
174+
public class Product
175+
{
176+
public int Id { get; set; }
177+
[Required]
178+
public string Name { get; set; } = "Default Name";
179+
public decimal Price { get; set; }
180+
public int Quantity { get; set; }
130181
}
131182
}
132183
````
@@ -136,94 +187,154 @@ Use [Predefined Confirm Dialog]({%slug dialog-predefined%}#confirm) with the des
136187
Using the [Dialog component]({%slug dialog-overview%}) will let you have fully customized Delete Confirmation Dialog. To handle the scenario:
137188

138189
* 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.
139-
* Handle the [`OnDelete`]({%slug grid-events%}#cud-events) event of the Grid to cancel the built-in delete, show the custom Dialog and get the current item (save the current item, so you can then use its details in the dialog if needed).
190+
* Use [custom commands]({%slug components/grid/columns/command%}) instead of the built-in `Save` and `Delete` commands to obtain the data item and show the Dialog component.
140191
* Handle the Dialog button clicks:
141-
* Proceed with the item deletion in the Confirm button click handler.
142-
* Hide the Dialog on Cancel.
192+
* Proceed with the item deletion in the Confirm button click handler.
193+
* Hide the Dialog on Cancel. Optionally, [exit Grid edit mode programmatically]({%slug grid-kb-add-edit-state%}).
194+
* The same approach is applicable for the `OnCreate` and `OnUpdate` events.
143195

144196
````CSHTML
197+
@using System.ComponentModel.DataAnnotations
198+
145199
<TelerikGrid @ref="@GridRef"
146-
Data=@GridData
200+
Data="@GridData"
147201
EditMode="@GridEditMode.Inline"
202+
OnCancel="@OnGridCancel"
148203
Pageable="true"
149-
OnDelete="@DeleteHandler">
204+
PageSize="20"
205+
Height="90vh">
150206
<GridColumns>
151-
<GridColumn Field=@nameof(SampleData.ID) Title="ID" Editable="false" />
152-
<GridColumn Field=@nameof(SampleData.Name) Title="Name" />
207+
<GridColumn Field="@nameof(Product.Name)" />
208+
<GridColumn Field="@nameof(Product.Price)" DisplayFormat="{0:C2}" />
209+
<GridColumn Field="@nameof(Product.Quantity)" />
153210
<GridCommandColumn>
154-
<GridCommandButton Command="Delete" Icon="@SvgIcon.Trash">Delete</GridCommandButton>
211+
<GridCommandButton Command="Edit">Edit</GridCommandButton>
212+
<GridCommandButton Command="CustomSave"
213+
OnClick="@OnGridSave"
214+
ShowInEdit="true">Save</GridCommandButton>
215+
<GridCommandButton Command="Cancel" ShowInEdit="true">Cancel</GridCommandButton>
216+
<GridCommandButton Command="CustomDelete"
217+
OnClick="@OnGridDelete">Delete</GridCommandButton>
155218
</GridCommandColumn>
156219
</GridColumns>
157220
</TelerikGrid>
158221
159222
<TelerikDialog @bind-Visible="@DialogVisible"
160-
Title="@Title">
223+
Width="300px"
224+
ButtonsLayout="@DialogButtonsLayout.End">
225+
<DialogTitle>
226+
Confirm Data Change
227+
</DialogTitle>
161228
<DialogContent>
162-
Are you sure you want to delete item with name: @CurrentItem.Name?
229+
@DialogContent
163230
</DialogContent>
164231
<DialogButtons>
165-
<TelerikButton OnClick="@DeleteItemFromDialog" ThemeColor="primary">Delete</TelerikButton>
166-
<TelerikButton OnClick="@(() => { DialogVisible = false; })">Cancel</TelerikButton>
232+
<TelerikButton OnClick="@( () => OnDialogButtonClick(true) )"
233+
ThemeColor="@ThemeConstants.Button.ThemeColor.Success">OK</TelerikButton>
234+
<TelerikButton OnClick="@( () => OnDialogButtonClick(false) )"
235+
ThemeColor="@ThemeConstants.Button.ThemeColor.Error">Cancel</TelerikButton>
167236
</DialogButtons>
168237
</TelerikDialog>
169238
170239
@code {
171-
private List<SampleData> GridData { get; set; }
172-
173-
private TelerikGrid<SampleData> GridRef;
240+
private TelerikGrid<Product>? GridRef { get; set; }
241+
private List<Product> GridData { get; set; } = new();
174242
175243
private bool DialogVisible { get; set; }
176244
177-
private string Title { get; set; } = "Custom delete confirmation";
245+
private Product? ItemToUpdate { get; set; }
246+
private Product? ItemToDelete { get; set; }
178247
179-
private SampleData CurrentItem { get; set; }
248+
private MarkupString DialogContent { get; set; } = new();
180249
181-
private async Task DeleteHandler(GridCommandEventArgs args)
250+
private int LastId { get; set; }
251+
252+
private void OnGridSave(GridCommandEventArgs args)
182253
{
183-
//get the current item from the args
184-
CurrentItem = (SampleData)args.Item;
254+
var itemToUpdate = (Product)args.Item;
255+
ItemToUpdate = itemToUpdate;
256+
257+
DialogContent = new MarkupString($"<p>Saving product <strong>{ItemToUpdate.Name}</strong>. <br /> Do you want to continue?</p>");
258+
DialogVisible = true;
259+
}
185260
186-
//cancel the built-in delete as you will handle the deletion in the dialog button click handler
187-
args.IsCancelled = true;
261+
private void OnGridDelete(GridCommandEventArgs args)
262+
{
263+
var deletedItem = (Product)args.Item;
264+
ItemToDelete = deletedItem;
188265
189-
//show the dialog
266+
DialogContent = new MarkupString($"<p>Deleting product <strong>{ItemToDelete.Name}</strong>. <br /> Do you want to continue?</p>");
190267
DialogVisible = true;
268+
}
191269
192-
//if needed, you can also customize the title to contain item details
193-
Title = "Custom title for " + CurrentItem.Name;
270+
private void OnGridCancel(GridCommandEventArgs args)
271+
{
272+
ItemToUpdate = null;
194273
}
195274
196-
private async Task DeleteItemFromDialog()
275+
private async Task OnDialogButtonClick(bool operationConfirmed)
197276
{
198-
//delete the item
199-
GridData.Remove(CurrentItem);
277+
if (operationConfirmed)
278+
{
279+
if (ItemToDelete != null)
280+
{
281+
GridData.Remove(ItemToDelete);
282+
}
283+
284+
if (ItemToUpdate != null)
285+
{
286+
var originalItemIndex = GridData.FindIndex(i => i.Id == ItemToUpdate.Id);
200287
201-
//refresh the Grid data
202-
GridRef.Rebind();
288+
if (originalItemIndex != -1)
289+
{
290+
GridData[originalItemIndex] = ItemToUpdate;
291+
}
292+
}
293+
294+
GridRef!.Rebind();
295+
}
296+
else if (ItemToUpdate != null)
297+
{
298+
var gridState = GridRef!.GetState();
299+
300+
gridState.EditItem = null!;
301+
gridState.OriginalEditItem = null!;
302+
303+
await GridRef!.SetStateAsync(gridState);
304+
}
203305
204-
//hide the dialog
205306
DialogVisible = false;
307+
308+
ItemToDelete = null;
309+
ItemToUpdate = null;
206310
}
207311
208-
protected override async Task OnInitializedAsync()
312+
protected override void OnInitialized()
209313
{
210-
GridData = new List<SampleData>();
211-
212-
for (int i = 0; i < 50; i++)
314+
for (int i = 1; i <= 30; i++)
213315
{
214-
GridData.Add(new SampleData()
215-
{
216-
ID = i,
217-
Name = "Name " + i.ToString()
218-
});
316+
GridData.Add(new Product()
317+
{
318+
Id = ++LastId,
319+
Name = $"Name {LastId}",
320+
Price = Random.Shared.Next(1, 100) * 1.23m,
321+
Quantity = Random.Shared.Next(0, 1000),
322+
});
219323
}
220324
}
221325
222-
// in a real case, keep the models in dedicated locations, this is just an easy to copy and see example
223-
public class SampleData
326+
public class Product
224327
{
225-
public int ID { get; set; }
226-
public string Name { get; set; }
328+
public int Id { get; set; }
329+
[Required]
330+
public string Name { get; set; } = "Default Name";
331+
public decimal Price { get; set; }
332+
public int Quantity { get; set; }
227333
}
228334
}
229335
````
336+
337+
## See Also
338+
339+
* [Grid Editing]({%slug components/grid/editing/overview%})
340+
* [Dialog Overview]({%slug dialog-overview%})

0 commit comments

Comments
 (0)