|
| 1 | +--- |
| 2 | +title: Add the Grid Built-In Functions When Using Grid Row Template |
| 3 | +description: Learn how to implement built-in Grid functions like CheckBoxColumn or CommandColumn when using the Grid's Row Template |
| 4 | +type: how-to |
| 5 | +page_title: Implement Built-in Functions when Using Grid Row Template |
| 6 | +slug: grid-kb-row-template-simulate-built-in-functions |
| 7 | +position: |
| 8 | +tags: grid, rowtemplate |
| 9 | +ticketid: 1463819, 1465447, 1578974, 1605222, 1606211, 1609036, 1629221, 1667096 |
| 10 | +res_type: kb |
| 11 | +--- |
| 12 | + |
| 13 | +## Environment |
| 14 | + |
| 15 | +<table> |
| 16 | + <tbody> |
| 17 | + <tr> |
| 18 | + <td>Product</td> |
| 19 | + <td>Grid for Blazor</td> |
| 20 | + </tr> |
| 21 | + </tbody> |
| 22 | +</table> |
| 23 | + |
| 24 | + |
| 25 | +## Description |
| 26 | + |
| 27 | +This KB article answers the following questions: |
| 28 | + |
| 29 | +* How to [select rows]({%slug grid-selection-row%}) in the Grid when using a [Row Template]({%slug grid-templates-row%})? |
| 30 | +* How to add a [checkbox column]({%slug components/grid/columns/checkbox%}) in the Grid when using a Row Template? I want to be able to select the row through its checkbox, but also to have the functionality to [select all rows]({%slug components/grid/columns/checkbox%}#parameters) from the header of the checkbox column. |
| 31 | +* Do the built-in keyboard options to select a range of rows by clicking the `Shift` or `Ctrl` key work when using a Row Template? How to check the checkbox of the row when I select a row by clicking the row? |
| 32 | +* How to add a [command column]({%slug components/grid/columns/command%}) in the Grid when using a Row Template? |
| 33 | +* When using a Row Template, how to prevent the selection of Grid rows when clicking on command buttons? |
| 34 | +* How to implement Grid column [resizing]({%slug components/grid/columns/resize%}), [auto-fitting]({%slug components/grid/columns/resize%}#autofit-columns), [visibility]({%slug grid-columns-visible%}), [locking]({%slug grid-columns-frozen%}), and [reordering]({%slug components/grid/columns/reorder%}) when using a Row Template? |
| 35 | + |
| 36 | +## Solution |
| 37 | + |
| 38 | +By default, using the Row Template disables most built-in functionalities of the Grid because the Grid no longer controls its own rendering. This lets you add custom implementations for these features. The [example below](#example) shows one way to implement functionalities such as row selection (both by clicking on a row and through a checkbox column), column resizing and visibility, editing through command buttons, sorting, and filtering. |
| 39 | + |
| 40 | +### Selection |
| 41 | + |
| 42 | +To implement custom selection functionality: |
| 43 | + |
| 44 | +* In the [`<GridColumns>` collection]({%slug components/grid/columns/bound%}#show-data-in-a-grid) add the [`<GridCheckboxColumn>`]({%slug components/grid/columns/checkbox%}) and use the [`HeaderTemplate`]({%slug components/grid/columns/checkbox%}#header-template) to add a [CheckBox component]({%slug checkbox-overview%}). This CheckBox component handles the [select all rows]({%slug components/grid/columns/checkbox%}#parameters) functionallity. |
| 45 | +* In the Row Template, add a `<td>` element with a CheckBox component. Add a Boolean property to the Grid model to indicate selection so you can use it for the state of this CheckBox. |
| 46 | +* Handle the Grid's [`SelectedItemsChanged` event]({%slug grid-selection-row%}#selecteditemschanged-event) and the CheckBox's [`OnChange` event]({%slug checkbox-events%}#onchange) to manage the [`SelectedItems` collection]({%slug grid-selection-overview%}#access-selected-rows-or-cells). The `OnChange` event fires after the `SelectedItemsChanged` event. In this case, you need to create a separate collection of selected items to persist the selected items when multiselecting through the checkbox column. |
| 47 | + |
| 48 | +### Editing, Sorting, Filtering |
| 49 | + |
| 50 | +The built-in editing, sorting, and filtering will work if the Row Template structure is similar to an actual table and only for the first Grid data model property included in the `<td>` element, if any. |
| 51 | + |
| 52 | +### Command Column |
| 53 | + |
| 54 | +To implement a custom command column: |
| 55 | + |
| 56 | +* In the `<GridColumns>` collection add the [`<GridCommandColumn>`]({%slug components/grid/columns/command%}) and use the [built-in `Save` and `Cancel` commands]({%slug components/grid/columns/command%}#built-in-commands). |
| 57 | +* In the Row Template add a `<td>` element with a [Button component]({%slug components/button/overview%}) and handle the Grid items editing and deleting programmatically. Refer to the knowledge base article on how to [enter and exit Grid edit mode programmatically]({%slug grid-kb-add-edit-state%}). |
| 58 | +* Set the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Event/stopPropagation" target="_blank">`stopPropagation` method</a> of the <a href="https://www.w3schools.com/jsref/event_onclick.asp" target="_blank">`onclick` event</a> to the `<td>` element to prevent row selection when clicking a command button. |
| 59 | + |
| 60 | +### Column Resizing, Auto-Fitting, Visibility, Locking, Reordering |
| 61 | + |
| 62 | +* Column resizing and auto-fitting will work if the Row Template structure resembles an actual table row, with a corresponding number of cells matching the Grid columns. |
| 63 | +* Column visibility depends on including a `<td>` element for the column in the Row Template. |
| 64 | +* To implement column locking, add the `k-grid-content-sticky` class to the `<td>` element of the columns that you want locked, and calculate and set the correct `left` and `right` CSS properties, as the content inside the Row Template can be any valid HTML. |
| 65 | +* For column reordering, manage the `left` and `right` CSS properties on the `<td>` elements within the Row Template. |
| 66 | + |
| 67 | +## Example |
| 68 | + |
| 69 | +>caption Row selection, Column resizing and visibility, Editing, Sorting, and Filtering when using Row Template |
| 70 | +
|
| 71 | +````CSHTML |
| 72 | +<TelerikGrid @ref="@GridRef" |
| 73 | + Data=@GridData |
| 74 | + Pageable="true" |
| 75 | + PageSize="15" |
| 76 | + Sortable="true" |
| 77 | + Resizable="true" |
| 78 | + FilterMode="@GridFilterMode.FilterMenu" |
| 79 | + FilterMenuType="@FilterMenuType.CheckBoxList" |
| 80 | + EditMode="@GridEditMode.Inline" |
| 81 | + SelectionMode="@GridSelectionMode.Multiple" |
| 82 | + SelectedItems="@SelectedItems" |
| 83 | + SelectedItemsChanged="@( (IEnumerable<ArticleDto> newSelected) => SelectedItemsChangedHandler(newSelected) )" |
| 84 | + OnCreate="@OnCreateHandler" |
| 85 | + OnUpdate="@OnUpdateHandler"> |
| 86 | + <GridToolBarTemplate> |
| 87 | + <GridCommandButton Command="Add">Add</GridCommandButton> |
| 88 | + </GridToolBarTemplate> |
| 89 | + <RowTemplate Context="article"> |
| 90 | + <td> |
| 91 | + <TelerikCheckBox @bind-Value="@article.IsSelected" OnChange="@(() => OnCheckBoxChangeHandler(article.Id))" /> |
| 92 | + </td> |
| 93 | + <td> |
| 94 | + <img src="@article.ImageUrl" width="55" height="35" /> |
| 95 | + </td> |
| 96 | + <td> |
| 97 | + @{ |
| 98 | + if(article.IsSelected) |
| 99 | + { |
| 100 | + <h2>Selected</h2> |
| 101 | + } |
| 102 | + else |
| 103 | + { |
| 104 | + <h2>Not Selected</h2> |
| 105 | + } |
| 106 | + } |
| 107 | + @article.Title |
| 108 | + </td> |
| 109 | + <td @onclick:stopPropagation="true"> |
| 110 | + <TelerikButton Icon="@SvgIcon.Pencil" OnClick="@(() => OnProgrammaticEditHandler(article.Id))">Programmatic Edit</TelerikButton> |
| 111 | + <TelerikButton Icon="@SvgIcon.Trash" OnClick="@(() => OnProgrammaticDeleteHandler(article.Id))">Programmatic Delete</TelerikButton> |
| 112 | + </td> |
| 113 | + </RowTemplate> |
| 114 | + <GridColumns> |
| 115 | + <GridCheckboxColumn> |
| 116 | + <HeaderTemplate> |
| 117 | + <TelerikCheckBox Value="@SelectAll" ValueChanged="@((bool value) => SelectAllHandler(value))" /> |
| 118 | + </HeaderTemplate> |
| 119 | + </GridCheckboxColumn> |
| 120 | + <GridColumn Field=@nameof(ArticleDto.Id) Visible="false" /> |
| 121 | + <GridColumn Field=@nameof(ArticleDto.ImageUrl) Title="Image" Editable="false" Resizable="true" /> |
| 122 | + <GridColumn Field=@nameof(ArticleDto.Title) Title="Article Title" Resizable="true" /> |
| 123 | + <GridCommandColumn> |
| 124 | + <GridCommandButton Icon="SvgIcon.Save" Command="Save" ShowInEdit="true">Update</GridCommandButton> |
| 125 | + <GridCommandButton Icon="SvgIcon.Cancel" Command="Cancel" ShowInEdit="true">Cancel</GridCommandButton> |
| 126 | + </GridCommandColumn> |
| 127 | + </GridColumns> |
| 128 | +</TelerikGrid> |
| 129 | +
|
| 130 | +@code { |
| 131 | + private TelerikGrid<ArticleDto>? GridRef { get; set; } |
| 132 | +
|
| 133 | + private List<ArticleDto> GridData { get; set; } = new(); |
| 134 | +
|
| 135 | + private IEnumerable<ArticleDto> SelectedItems { get; set; } = Enumerable.Empty<ArticleDto>(); |
| 136 | + private List<ArticleDto> TempSelectedItemsCollection { get; set; } = new(); |
| 137 | + private bool SelectAll { get; set; } |
| 138 | +
|
| 139 | + #region Selection |
| 140 | +
|
| 141 | + private void SelectAllHandler(bool newValue) |
| 142 | + { |
| 143 | + SelectAll = newValue; |
| 144 | +
|
| 145 | + foreach (var item in GridData) |
| 146 | + { |
| 147 | + item.IsSelected = SelectAll; |
| 148 | + } |
| 149 | +
|
| 150 | + // If SelectAll is true, assign all items to SelectedItems, |
| 151 | + // else set it to an empty list. |
| 152 | + SelectedItems = SelectAll ? new List<ArticleDto>(GridData) : new List<ArticleDto>(); |
| 153 | + TempSelectedItemsCollection = SelectAll ? new List<ArticleDto>(GridData) : new List<ArticleDto>(); |
| 154 | + } |
| 155 | +
|
| 156 | + protected void SelectedItemsChangedHandler(IEnumerable<ArticleDto> selectedItems) |
| 157 | + { |
| 158 | + foreach (var item in GridData) |
| 159 | + { |
| 160 | + item.IsSelected = false; |
| 161 | + } |
| 162 | +
|
| 163 | + // Use temporary collection to be able to persist the |
| 164 | + // selected items when multiselecting with checkboxes. |
| 165 | + TempSelectedItemsCollection = SelectedItems.ToList(); |
| 166 | + SelectedItems = selectedItems; |
| 167 | +
|
| 168 | + foreach (var item in selectedItems) |
| 169 | + { |
| 170 | + item.IsSelected = true; |
| 171 | + } |
| 172 | + } |
| 173 | +
|
| 174 | + private void OnCheckBoxChangeHandler(Guid itemId) |
| 175 | + { |
| 176 | + ArticleDto? currentItem = GridData.FirstOrDefault(a => a.Id == itemId); |
| 177 | +
|
| 178 | + if (currentItem != null) |
| 179 | + { |
| 180 | + if (currentItem.IsSelected) |
| 181 | + { |
| 182 | + TempSelectedItemsCollection.Add(currentItem); |
| 183 | + } |
| 184 | + else |
| 185 | + { |
| 186 | + TempSelectedItemsCollection.Remove(currentItem); |
| 187 | + } |
| 188 | + } |
| 189 | +
|
| 190 | + // The OnChange event fires after the SelectedItemsChanged |
| 191 | + // thus we need to update the SelectedItems collection. |
| 192 | + SelectedItems = TempSelectedItemsCollection; |
| 193 | +
|
| 194 | + foreach (var item in SelectedItems) |
| 195 | + { |
| 196 | + item.IsSelected = true; |
| 197 | + } |
| 198 | + } |
| 199 | +
|
| 200 | + #endregion Selection |
| 201 | +
|
| 202 | + #region Edit |
| 203 | +
|
| 204 | + private void OnCreateHandler(GridCommandEventArgs args) |
| 205 | + { |
| 206 | + var createdItem = (ArticleDto)args.Item; |
| 207 | + createdItem.Id = Guid.NewGuid(); |
| 208 | + var rnd = new Random(); |
| 209 | + createdItem.ImageUrl = $"https://demos.telerik.com/blazor-ui/images/photos/{rnd.Next(1, 30) % 7 + 1}.jpg"; |
| 210 | + GridData.Insert(0, createdItem); |
| 211 | + } |
| 212 | +
|
| 213 | + private async Task OnProgrammaticEditHandler(Guid itemId) |
| 214 | + { |
| 215 | + if (GridData.Any() && GridRef != null) |
| 216 | + { |
| 217 | + var gridState = GridRef.GetState(); |
| 218 | +
|
| 219 | + gridState.InsertedItem = null; |
| 220 | + gridState.OriginalEditItem = GridData.Where(x => x.Id == itemId).First(); |
| 221 | + gridState.EditItem = GridData.Where(x => x.Id == itemId).First().Clone(); |
| 222 | + var rnd = new Random(); |
| 223 | + gridState.EditItem.ImageUrl = $"https://demos.telerik.com/blazor-ui/images/photos/{rnd.Next(1, 30) % 7 + 1}.jpg"; |
| 224 | + await GridRef.SetStateAsync(gridState); |
| 225 | + } |
| 226 | + } |
| 227 | +
|
| 228 | + private void OnUpdateHandler(GridCommandEventArgs args) |
| 229 | + { |
| 230 | + var updatedItem = (ArticleDto)args.Item; |
| 231 | + var index = GridData.FindIndex(i => i.Id == updatedItem.Id); |
| 232 | + if (index != -1) |
| 233 | + { |
| 234 | + GridData[index] = updatedItem; |
| 235 | + } |
| 236 | + } |
| 237 | +
|
| 238 | + private void OnProgrammaticDeleteHandler(Guid itemId) |
| 239 | + { |
| 240 | + if (GridData.Any() && GridRef != null) |
| 241 | + { |
| 242 | + var itemToDelete = GridData.Where(x => x.Id == itemId).First(); |
| 243 | + GridData.Remove(itemToDelete); |
| 244 | +
|
| 245 | + // Remove from SelectedItems collection |
| 246 | + TempSelectedItemsCollection = SelectedItems.ToList(); |
| 247 | + TempSelectedItemsCollection.Remove(itemToDelete); |
| 248 | + SelectedItems = TempSelectedItemsCollection; |
| 249 | +
|
| 250 | + GridRef.Rebind(); |
| 251 | + } |
| 252 | + } |
| 253 | +
|
| 254 | + #endregion Edit |
| 255 | +
|
| 256 | + #region Data Generation |
| 257 | +
|
| 258 | + private void GetGridData() |
| 259 | + { |
| 260 | + GridData = new List<ArticleDto>(); |
| 261 | +
|
| 262 | + for (int i = 1; i <= 30; i++) |
| 263 | + { |
| 264 | + GridData.Add(new ArticleDto |
| 265 | + { |
| 266 | + Id = Guid.NewGuid(), |
| 267 | + Title = "Article title " + i, |
| 268 | + ImageUrl = $"https://demos.telerik.com/blazor-ui/images/photos/{i % 7 + 1}.jpg" |
| 269 | + }); |
| 270 | + } |
| 271 | + } |
| 272 | +
|
| 273 | + protected override void OnInitialized() |
| 274 | + { |
| 275 | + GetGridData(); |
| 276 | + } |
| 277 | +
|
| 278 | + public class ArticleDto |
| 279 | + { |
| 280 | + public Guid Id { get; set; } |
| 281 | + public string Title { get; set; } |
| 282 | + public string ImageUrl { get; set; } |
| 283 | + public bool IsSelected { get; set; } |
| 284 | +
|
| 285 | + public ArticleDto Clone() |
| 286 | + { |
| 287 | + return new ArticleDto() |
| 288 | + { |
| 289 | + Id = Id, |
| 290 | + Title = Title, |
| 291 | + ImageUrl = ImageUrl |
| 292 | + }; |
| 293 | + } |
| 294 | + } |
| 295 | +
|
| 296 | + #endregion DataGeneration |
| 297 | +
|
| 298 | +} |
| 299 | +```` |
| 300 | + |
| 301 | +## See Also |
| 302 | + |
| 303 | +* [Grid Row Template]({%slug grid-templates-row%}) |
| 304 | +* [Grid Row Selection]({%slug grid-selection-row%}) |
| 305 | +* [Grid Command Column]({%slug components/grid/columns/command%}) |
| 306 | +* [Grid Column Resizing]({%slug components/grid/columns/resize%}) |
| 307 | +* [Grid Column Auto-fitting]({%slug components/grid/columns/resize%}#autofit-columns) |
| 308 | +* [Grid Column Visibility]({%slug grid-columns-visible%}) |
| 309 | +* [Grid Column Locking]({%slug grid-columns-frozen%}) |
| 310 | +* [Grid Column Reordering]({%slug components/grid/columns/reorder%}) |
| 311 | +* [Enter And Exit Grid Edit Mode Programmatically]({%slug grid-kb-add-edit-state%}) |
0 commit comments