Initial groups are duplicated in version 6.1.0. The OnStateInit event fires twice and the group descriptors are added twice. The problem occurs only when using <GridAggregates>.
The workaround is to clear the GroupDescriptors collection on OnStateInit.
@using Telerik.DataSource
<h2>No Aggregates</h2>
<p><code>OnStateInitCounter1:</code> @OnStateInitCounter1</p>
<TelerikGrid Data="@GridData"
TItem="@SampleModel"
Groupable="true"
OnStateInit="@OnGridStateInit1">
<GridAggregates>
</GridAggregates>
<GridColumns>
<GridColumn Field="@nameof(SampleModel.Name)" />
<GridColumn Field="@nameof(SampleModel.GroupName)" />
</GridColumns>
</TelerikGrid>
<h2>With Aggregates</h2>
<p><code>OnStateInitCounter2:</code> @OnStateInitCounter2</p>
<TelerikGrid Data="@GridData"
TItem="@SampleModel"
Groupable="true"
OnStateInit="@OnGridStateInit2">
<GridAggregates>
<GridAggregate Field="@nameof(SampleModel.Name)" Aggregate="@GridAggregateType.Count" />
</GridAggregates>
<GridColumns>
<GridColumn Field="@nameof(SampleModel.Name)" />
<GridColumn Field="@nameof(SampleModel.GroupName)" />
</GridColumns>
</TelerikGrid>
<h2>With Aggregates and Workaround</h2>
<p><code>OnStateInitCounter3:</code> @OnStateInitCounter3</p>
<TelerikGrid Data="@GridData"
TItem="@SampleModel"
Groupable="true"
OnStateInit="@OnGridStateInit3">
<GridAggregates>
<GridAggregate Field="@nameof(SampleModel.Name)" Aggregate="@GridAggregateType.Count" />
</GridAggregates>
<GridColumns>
<GridColumn Field="@nameof(SampleModel.Name)" />
<GridColumn Field="@nameof(SampleModel.GroupName)" />
</GridColumns>
</TelerikGrid>
@code {
private int OnStateInitCounter1 { get; set; }
private int OnStateInitCounter2 { get; set; }
private int OnStateInitCounter3 { get; set; }
private void OnGridStateInit1(GridStateEventArgs<SampleModel> args)
{
++OnStateInitCounter1;
args.GridState.GroupDescriptors.Add(new GroupDescriptor
{
Member = nameof(SampleModel.GroupName)
});
}
private void OnGridStateInit2(GridStateEventArgs<SampleModel> args)
{
++OnStateInitCounter2;
args.GridState.GroupDescriptors.Add(new GroupDescriptor
{
Member = nameof(SampleModel.GroupName)
});
}
private void OnGridStateInit3(GridStateEventArgs<SampleModel> args)
{
++OnStateInitCounter3;
args.GridState.GroupDescriptors = new List<GroupDescriptor>();
// OR
args.GridState.GroupDescriptors.Clear();
args.GridState.GroupDescriptors.Add(new GroupDescriptor
{
Member = nameof(SampleModel.GroupName)
});
}
private List<SampleModel> GridData { get; set; } = new();
protected override void OnInitialized()
{
for (int i = 1; i <= 4; i++)
{
GridData.Add(new SampleModel()
{
Id = i,
Name = $"Name {i}",
GroupName = $"Group {i % 2 + 1}"
});
}
}
public class SampleModel
{
public int Id { get; set; }
public string Name { get; set; } = string.Empty;
public string GroupName { get; set; } = string.Empty;
}
}
When trying to make a cell selection using Shift + Click, the clicked cells are not correctly added to the selection range. When clicking on adjacent cells, the selection range gets misplaced resulting in deselection of the first selected cells.
In a filterable Grid, if a column is not bound to a field from the model the Grid uses, it throws with:
Error: System.ArgumentNullException: Value cannot be null. (Parameter 'nullableType')
Reproduction: https://blazorrepl.telerik.com/GyEUlFEs04AUJoJ601.
The issue is reproducible :
===
ADMIN EDIT
===
A possible workaround for the time being is to set the FieldType of the column: https://blazorrepl.telerik.com/wSugvFui10jhcpZy00.
Reproduceable example: https://blazorrepl.telerik.com/QSEganvg12BVw2ZU37
Steps to reproduce:
If you remove the GridAggregates emtpy render fragment, or put at least one GridAggregate component in it, OnStateInit fires like expected.
In my project, I've created a component that wraps around the TelerikGrid that I use on all my pages, so that I can have many properties and functions set to reasonable defaults for my use case. My component has an option to conditionally include GridAggregate components within the inner TelerikGrid's GridAggregates render fragment based on separate configuration. If no separate configuration is provided, no GridAggregate components are included, and the GridAggregates render fragment is left blank. Since my component is built in razor markup, there isn't a great option for nulling out that render fragment if it's empty. I had no problems with this exact same code in UI for Blazor version 5.1, and I hope this can be fixed.
I have a Grid using checkbox selection and when I un-check a row, the SelectedItemsChanged event is not firing. I'm using an async handler.
The problem appears to happen when a row is selected and then un-selected. The event is firing when it is selected but is not firing when it is un-selected.
<TelerikGrid Data="strategicLevelItems" SelectedItems="selectedStrategic" Width="100%" Height="500px"
ScrollMode="GridScrollMode.Scrollable" SelectionMode="GridSelectionMode.Multiple"
SelectedItemsChanged="@((IEnumerable<GetNavigationNodesModel> strategicItems) => OnStrategicSelectAsync(strategicItems))"
FilterMode="GridFilterMode.FilterRow">
<GridColumns>
<GridCheckboxColumn SelectAll="true" Width="40px" OnCellRender="@GridHelpers.CenterAlign" />
<GridColumn Field="@(nameof(GetNavigationNodesModel.Name))" />
</GridColumns>
</TelerikGrid>
private async Task OnStrategicSelectAsync(IEnumerable<GetNavigationNodesModel> selectedItems)
{
selectedStrategic = selectedItems;
var state = tacticalGrid.GetState();
var compositeFilter = new CompositeFilterDescriptor() { LogicalOperator = FilterCompositionLogicalOperator.Or };
foreach (var item in selectedItems)
{
compositeFilter.FilterDescriptors.Add(new FilterDescriptor()
{
Member = "ParentId",
Operator = FilterOperator.IsEqualTo,
Value = item.Id
});
}
state.FilterDescriptors.Clear();
state.FilterDescriptors.Add(compositeFilter);
await tacticalGrid.SetState(state);
}
---
ADMIN EDIT
Here is a workaround - using a bit of JS to clear the checked state of the checkbox element when you clear the SelectedItems collection:
At the end of this post you can find attached a sample project that showcases a more complex scenario where you may want to keep some rows selected but cancel the selection of others (in that sample, only one row with a given ItemType can be selected). It shows how you can get the index in the rendering of a particular row to use for a slightly modified version of the JS function.
@inject IJSRuntime _js
@* Move JavaScript code to a separate JS file in production *@
<script suppress-error="BL9992">
function uncheckGrid() {
var checkboxes = document.querySelectorAll(".no-select .k-grid-checkbox");
for (var i = 0; i < checkboxes.length; i++) {
checkboxes[i].checked = false;
}
}
</script>
<style>
.no-select {
cursor: not-allowed;
}
.no-select .k-checkbox {
pointer-events: none;
}
</style>
<TelerikGrid Data=@GridData
SelectionMode="@GridSelectionMode.Multiple"
SelectedItemsChanged="@((IEnumerable<Employee> employeeList) => OnSelect(employeeList))"
SelectedItems="@SelectedEmployees"
Pageable="true"
Height="400px">
<GridColumns>
<GridCheckboxColumn SelectAll="true" OnCellRender="@OnGridCellRender" />
<GridColumn Field="@nameof(Employee.Name)" />
<GridColumn Field="@nameof(Employee.Team)" />
<GridColumn Field="@nameof(Employee.Active)" />
</GridColumns>
</TelerikGrid>
@if (SelectedEmployees != null)
{
<ul>
@foreach (Employee employee in SelectedEmployees)
{
<li>
@employee.Name
</li>
}
</ul>
}
@code {
private List<Employee> GridData { get; set; } = new List<Employee>();
private IEnumerable<Employee> SelectedEmployees { get; set; } = new List<Employee>();
private void OnGridCellRender(GridCellRenderEventArgs args)
{
var product = (Employee)args.Item;
if (!product.Active)
{
args.Class = "no-select";
}
}
protected void OnSelect(IEnumerable<Employee> newSelected)
{
var validItemsToSelect = newSelected.Where(x => x.Active);
SelectedEmployees = validItemsToSelect;
if (validItemsToSelect.Count() < newSelected.Count())
{
_js.InvokeVoidAsync("uncheckGrid");
}
}
protected override void OnInitialized()
{
for (int i = 1; i <= 7; i++)
{
GridData.Add(new Employee()
{
EmployeeId = i,
Name = $"Employee {i}",
Team = $"Team {i % 3 + 1}",
Active = i % 2 == 0
});
}
}
public class Employee
{
public int EmployeeId { get; set; }
public string Name { get; set; } = string.Empty;
public string Team { get; set; } = string.Empty;
public bool Active { get; set; }
}
}
The Grid will throw a JavaScript error if the user wants to group and drops a column header near the edges of the grouping header or between existing group chips.
This is a regression in version 6.0.0.
REPL test page: https://blazorrepl.telerik.com/wouBdbvw36O4UaAR14
A possible workaround is to remove the empty space, which triggers the error, so that users cannot drop on it:
CSS
.k-grid .k-grouping-header {
padding: 0;
border-bottom-width: 0;
gap: 0;
}
.k-grid .k-grouping-header::before {
margin: 0;
}
.k-grid .k-grouping-header .k-grouping-drop-container {
margin: 0;
}
I have a grid with the excell Export. Now I have two custom Export buttons on the Page (Over and under the Grid).
Can I somehow trigger the Grid Excell Export command Manually?
---
ADMIN EDIT
Here is a workaround - a bit of JS to click the desired button and the second snippet shows how to invoke it from a blazor component and code.
<script>
function clickButton(selector) {
var btn = document.querySelector(selector);
if (btn && btn.click) {
btn.click();
}
}
</script>
@inject IJSRuntime _js
<style>
/* hide the built-in button if you like */
.myExcelExportButton {
display:none;
}
</style>
<button @onclick="@MyExternalExportTrigger">my external export button</button>
<TelerikGrid Data="@GridData" Pageable="true" Sortable="true" Resizable="true" Reorderable="true"
FilterMode="@GridFilterMode.FilterRow" Groupable="true"
Class="@GridClass">
<GridToolBar>
<GridCommandButton Class="@ExportBtnClass" Command="ExcelExport" Icon="@IconName.FileExcel">Export to Excel</GridCommandButton>
<label><TelerikCheckBox @bind-Value="@ExportAllPages" />Export All Pages</label>
</GridToolBar>
<GridExport>
<GridExcelExport FileName="telerik-grid-export" AllPages="@ExportAllPages" />
</GridExport>
<GridColumns>
<GridColumn Field="@nameof(SampleData.ProductId)" Title="ID" Width="100px" />
<GridColumn Field="@nameof(SampleData.ProductName)" Title="Product Name" Width="300px" />
<GridColumn Field="@nameof(SampleData.UnitsInStock)" Title="In stock" Width="100px" />
<GridColumn Field="@nameof(SampleData.Price)" Title="Unit Price" Width="200px" />
<GridColumn Field="@nameof(SampleData.Discontinued)" Title="Discontinued" Width="100px" />
<GridColumn Field="@nameof(SampleData.FirstReleaseDate)" Title="Release Date" Width="300px" />
</GridColumns>
</TelerikGrid>
@code {
// cascade through classes on the grid and/or on the built-in button to click it with JS
// so that it can invoke the built-in export operations
string GridClass { get; set; } = "MyGrid";
string ExportBtnClass { get; set; } = "myExcelExportButton";
async Task MyExternalExportTrigger()
{
var selector = $".{GridClass} .k-header .{ExportBtnClass}";
await _js.InvokeVoidAsync("clickButton", selector);
}
List<SampleData> GridData { get; set; }
bool ExportAllPages { get; set; }
protected override void OnInitialized()
{
GridData = Enumerable.Range(1, 100).Select(x => new SampleData
{
ProductId = x,
ProductName = $"Product {x}",
UnitsInStock = x * 2,
Price = 3.14159m * x,
Discontinued = x % 4 == 0,
FirstReleaseDate = DateTime.Now.AddDays(-x)
}).ToList();
}
public class SampleData
{
public int ProductId { get; set; }
public string ProductName { get; set; }
public int UnitsInStock { get; set; }
public decimal Price { get; set; }
public bool Discontinued { get; set; }
public DateTime FirstReleaseDate { get; set; }
}
}
---
Please add an attribute to Blazor GridColumn which allows to easily format data with the default .NET standard formats
e.g. <GridColumn Field="@(nameof(Item.Price))" Title="Price" Format="0#.##" />
It's a little bit annoying always having to define a template for this purpose.
When you combine frozen column with row selection and horizontal scrolling is applied (due to column and grid widths), column content are not hidden under frozen columns when a row is selected, as seen bellow in lines 2,3 and 4.
Checkbox, Product Name & Command columns are frozen. "Quantity per Unit" values are clearly visible behind frozen "Product Name" values. You can find a test scenario here (Telerik REPL for Blazor).
All themes suffer from this issue except Fluent theme. With default and bootstrap themes it happens on all odd selected lines and with material theme it happens on all selected lines.
Grid headers are misaligned if they are navigated in a scrolling scenario with a Frozen column.
===
ADMIN EDIT
===
The behavior affects the TreeList as well in a similar fashion. The misalignment is bigger when there is a frozen column but it is also reproducible without it in this demo.
The null type of operator can cause errors on the backend
*** Thread created by admin on customer behalf ***
Hi Telerik team,
When I want to bind the Blazor Grid to an ExpandoObject I need to assign the FieldType. I know I have to use non-nullable types.
But I want to use nullable types (for example int?) to have a blank cell within the Grid when there is no value for it. With "typeof(int)" instead of "typeof(int?)" every empty cell shows "0" which I don't want.
Is there any chance to let FieldType accept nullable types?
Best regards,
Rayko