I am having problems with grouping in TelerikGrid.
When I drag the column into the header for grouping, I get an error ArgumentNullException: Value cannot be null, apparently, somewhere in TelerikGrid.SetProcessedGroups.
Test code:@page "/test"
<div>
<TelerikGrid @ref="grid" Data="@GridData" @bind-Page="Page" PageSize="PageSize"
Pageable="true" Sortable="true" Groupable="true"
FilterMode="Telerik.Blazor.GridFilterMode.FilterMenu"
ShowColumnMenu="true"
TotalCount="@TotalRowCount"
SelectionMode="GridSelectionMode.Single"
OnRead=@ReadData>
<GridAggregates>
<GridAggregate Field=@nameof(TestData.Name) Aggregate="@GridAggregateType.Count" />
<GridAggregate Field=@nameof(TestData.Surname) Aggregate="@GridAggregateType.Count" />
<GridAggregate Field=@nameof(TestData.Department) Aggregate="@GridAggregateType.Count" />
</GridAggregates>
<GridColumns>
<GridColumn Field="@(nameof(TestData.Name))" FieldType="typeof(string)" Editable="false" Groupable="true" ShowColumnMenu="false" />
<GridColumn Field="@(nameof(TestData.Surname))" FieldType="typeof(string)" Editable="false" Groupable="true" ShowColumnMenu="false" />
<GridColumn Field="@(nameof(TestData.Department))" FieldType="typeof(string)" Editable="false" Groupable="true" ShowColumnMenu="false" />
<GridCommandColumn Width="60px" Lockable="false" ShowColumnMenu="true">
<GridCommandButton Command="View" Icon="information" ShowInEdit="false" OnClick="@ViewItem"></GridCommandButton>
</GridCommandColumn>
</GridColumns>
</TelerikGrid>
</div>
@code {
protected TelerikGrid<TestData> grid;
protected List<TestData> GridData { get; set; }
public int TotalRowCount { get; set; } = 0;
public int Page { get; set; } = 1;
public int PageSize { get; set; } = 10;
protected async Task ReadData(GridReadEventArgs args)
{
await LoadData();
}
protected async Task LoadData()
{
GridData = await GetSampleData();
TotalRowCount = 100;
}
protected async Task<List<TestData>> GetSampleData()
{
List<TestData> results = new List<TestData>();
results.Add(new TestData { Name = "Name1", Surname = "Surname1", Department = "Department1" });
results.Add(new TestData { Name = "Name2", Surname = "Surname2", Department = "Department1" });
results.Add(new TestData { Name = "Name3", Surname = "Surname3", Department = "Department1" });
results.Add(new TestData { Name = "Name4", Surname = "Surname4", Department = "Department1" });
results.Add(new TestData { Name = "Name5", Surname = "Surname5", Department = "Department2" });
results.Add(new TestData { Name = "Name6", Surname = "Surname6", Department = "Department2" });
results.Add(new TestData { Name = "Name7", Surname = "Surname7", Department = "Department2" });
results.Add(new TestData { Name = "Name8", Surname = "Surname8", Department = "Department3" });
results.Add(new TestData { Name = "Name9", Surname = "Surname9", Department = "Department3" });
results.Add(new TestData { Name = "Name10", Surname = "Surname10", Department = "Department4" });
await Task.Delay(1000); //simulate loading delay ...
return results;
}
protected void ViewItem(GridCommandEventArgs args)
{
//navigate to edit view ...
}
public class TestData
{
public string Name { get; set; }
public string Surname { get; set; }
public string Department { get; set; }
}
}
I am getting the following error:
This happens in Chrome and Edge, in Webassembly and Server-side blazor.
Am I doing something wrong?
Thanks for the help!
How would you remove the icon to expand a detail grid only for certain rows? Some rows will not have detail data and should not be expandable.
---
ADMIN EDIT
As suggested by Joel, you can use the RowRender event and a bit of CSS to hide the button. Here is a Knolwdge Base article that shows a fully runnable sample: https://docs.telerik.com/blazor-ui/knowledge-base/grid-conditional-expand-button.
void OnRowRenderHandler(GridRowRenderEventArgs args)
{
OrgUnit item = args.Item as OrgUnit;
args.Class = item.Children.length ? "has-children" : "no-children";
}
tr.no-children .k-hierarchy-cell *{
display:none !important;
}
I'm trying manual source operations with grouping and aggregates, but I get: InvalidOperationException: No generic method 'Sum' on type 'System.Linq.Enumerable' is compatible with the supplied type arguments and arguments. No type arguments should be provided if the method is non-generic
I've edited the sample in the docs of manual source operations with grouping and added some aggregates.
Since some feats with the grid are not compatible with others, it is not easy to understand what is feasible and what is not. I know from the docs that virtual scolling is not compatible with many feats, or that group load on demand is not comaptible with aggregates, but I don't found any about aggregates with manual operations.
Please find the attached demo project
1. Use the code from the incell editing docs (https://docs.telerik.com/blazor-ui/components/grid/editing/incell)and monitor the server console
2. Edit a name (row 3 and onward)
3. Press Enter
I expect OnEdit to fire, but only OnUpdate fires
I'm getting error for dynamic Grid InCell editing. Error is displayed on editing before calling UpdateHandler. It is observed for adding new row or editing any row.
Error: System.ArgumentException: Instance property 'x' is not defined for type 'System.Dynamic.ExpandoObject' (Parameter 'propertyName')
I was not getting this error In previous version 2.21.0 on edit of a dynamic field. Getting this error after updating to new 2.23.0 version without any code change.
If I add an await-ed call in the OnRowClick handler, then I cannot alter the grid state later in the code. It only works if the method is called again (e.g., a second click on the same row).
<AdminEdit>
This affects other Grid events too, for example, the PageChanged
</AdminEdit>
A workaround is to use synchronous code (remove the await call):
@inject IJSRuntime JsInterop
<TelerikGrid Data="@salesTeamMembers" OnRowClick="@OnRowClickHandler" @ref="@GridRef">
<DetailTemplate>
@{
var employee = context as MainModel;
<TelerikGrid Data="employee.Orders" Pageable="true" PageSize="5">
<GridColumns>
<GridColumn Field="OrderId"></GridColumn>
<GridColumn Field="DealSize"></GridColumn>
</GridColumns>
</TelerikGrid>
}
</DetailTemplate>
<GridColumns>
<GridColumn Field="Id"></GridColumn>
<GridColumn Field="Name"></GridColumn>
</GridColumns>
</TelerikGrid>
@code {
List<MainModel> salesTeamMembers { get; set; }
TelerikGrid<MainModel> GridRef { get; set; }
async Task OnRowClickHandler(GridRowClickEventArgs args) {
// After adding this line, it now requires a double click when the InvokeAsync call uses "await"
var width = JsInterop.InvokeAsync<int>("getWidth");
var model = args.Item as MainModel;
int index = salesTeamMembers.IndexOf(model);
//todo: you may want to take paging into account for example, or use js interop to get the index of the row based on contents from it like id
if (index > -1) {
var state = GridRef.GetState();
state.ExpandedRows = new List<int> { index };
await GridRef.SetState(state);
}
}
protected override void OnInitialized() {
salesTeamMembers = GenerateData();
}
private List<MainModel> GenerateData() {
List<MainModel> data = new List<MainModel>();
for (int i = 0; i < 5; i++) {
MainModel mdl = new MainModel { Id = i, Name = $"Name {i}" };
mdl.Orders = Enumerable.Range(1, 15).Select(x => new DetailsModel { OrderId = x, DealSize = x ^ i }).ToList();
data.Add(mdl);
}
return data;
}
public class MainModel {
public int Id { get; set; }
public string Name { get; set; }
public List<DetailsModel> Orders { get; set; }
}
public class DetailsModel {
public int OrderId { get; set; }
public double DealSize { get; set; }
}
}
Select one or more rows
Right click another of the rows (there is code in the OnContextMenu handler that changes the selected items to the currently clicked row)
<TelerikContextMenu @ref="@ContextMenuRef" Data="@MenuItems" OnClick="@((MenuItem item) => OnItemClick(item))"></TelerikContextMenu>
<TelerikGrid Data=@GridData
@ref="Grid"
SelectionMode="GridSelectionMode.Multiple"
@bind-SelectedItems="@SelectedEmployees"
@bind-Page="@CurrentPage"
PageSize="@PageSize"
OnRowContextMenu="OnContextMenu"
Pageable="true">
<GridColumns>
<GridCheckboxColumn />
<GridColumn Field=@nameof(Employee.EmployeeId) />
<GridColumn Field=@nameof(Employee.Name) />
<GridColumn Field=@nameof(Employee.Team) />
</GridColumns>
</TelerikGrid>
@if (SelectedEmployees != null)
{
<ul>
@foreach (Employee employee in SelectedEmployees.OrderBy(e => e.EmployeeId))
{
<li>
@employee.EmployeeId
</li>
}
</ul>}
@code {
public IEnumerable<Employee> SelectedEmployees { get; set; } = Enumerable.Empty<Employee>();
TelerikContextMenu<MenuItem> ContextMenuRef { get; set; }
TelerikGrid<Employee> Grid { get; set; }
List<MenuItem> MenuItems { get; set; }
int CurrentPage { get; set; } = 1;
int PageSize { get; set; } = 5;
//data binding and sample data
public List<Employee> GridData { get; set; }
protected override void OnInitialized()
{
GridData = new List<Employee>();
for (int i = 0; i < 15; i++)
{
GridData.Add(new Employee()
{
EmployeeId = i,
Name = "Employee " + i.ToString(),
Team = "Team " + i % 3
});
}
MenuItems = new List<MenuItem>()
{
new MenuItem(){ Text = "Delete", Icon = IconName.Delete, CommandName = "Delete"}
};
}
protected async Task OnItemClick(MenuItem item)
{
if (item.Action != null)
{
item.Action.Invoke();
}
else
{
switch (item.CommandName)
{
case "Delete":
await Task.Delay(1); // do something
break;
}
}
}
protected async Task OnContextMenu(GridRowClickEventArgs args)
{
if (!(args.Item is Employee employee))
return;
SelectedEmployees = new List<Employee> { employee }; // this does not work
if (args.EventArgs is MouseEventArgs mouseEventArgs)
{
await ContextMenuRef.ShowAsync(mouseEventArgs.ClientX, mouseEventArgs.ClientY);
}
}
public class Employee
{
public int EmployeeId { get; set; }
public string Name { get; set; }
public string Team { get; set; }
}
public class MenuItem
{
public string Text { get; set; }
public string Icon { get; set; }
public Action Action { get; set; }
public string CommandName { get; set; }
}
}
*** Thread created by admin on customer behalf ***
---
ADMIN EDIT
At the end of this post you can find the two most common scenarios that can exhibit this problem, and a video that illustrates it.
A potential workaround could be to avoid the second call to onread because that's where the problem lies, with code similar to this:
int readCounts { get; set; }
protected async Task ReadItems(GridReadEventArgs args)
{
if (readCounts != 1) // the second call is skipped - that's where the problem lies
{
Console.WriteLine("ONREAD with skip " + args.Request.Skip);
HttpResponseMessage response = await Http.PostAsJsonAsync("WeatherForecast", args.Request);
if (response.StatusCode == System.Net.HttpStatusCode.OK)
{
DataEnvelope<WeatherForecast> envelope = await response.Content.ReadFromJsonAsync<DataEnvelope<WeatherForecast>>();
GridData = envelope.CurrentPageData;
Total = envelope.TotalItemCount;
StateHasChanged();
}
}
readCounts++;
}
where you may need to extend this a little to also use a flag for whether there was state loaded at all - if there wasn't, then the OnRead call may want to continue because it could be a legitimate user action. Whether this is needed is up to the app and whether your users already have a state saved (if you use OnStateChanged to save the state, they probably already do).
Another possible workaround is to always save Skip=0 in the state so that the user always starts at the top of the grid rather than juuust slightly off from where they expect.
---
The grid should support INotifyPropertyChanged for the objects in the grid, i.e. if the data object implements INotifyPropertyChanged and a property value changes, the corresponding cell value in the grid should also change.
I noticed after debugging a problem when editing some rather complex rows in a grid that the grid internally calls Telerik.Blazor.Extensions.ObjectExtensions.Clone(...) to create a deep clone for editing. If the object being cloned implements System.ICloneable then this is ignored and an attempt is made to clone it using reflection - the problem is that ObjectExtensions.Clone(...) fails for a number of cases and observing ICloneable would give a developer the means to implement the correct cloning behaviour.
I'm not asking for ObjectExtensions.Clone(...) to be fixed in any way (after all, no matter what you do there will always be some cases it cannot handle), just to use ICloneable where possible.
As an aside, the failed cases were:
Hello,
I am currently fiddling around with the Blazor UI, mainly the grid. I was wondering if it is possible to configure the grid to automatically fit the columns to the contents they have?
Meaning that the width of the header and the content cells of a given column are automatically set to a value which best fits either the cells content or the header title (depending on what takes up more space).
Best Regards,
Karl
When you resize grid columns the column width does not follow the mouse. This occurs in your demo:
https://demos.telerik.com/blazor-ui/grid/column-resizing
Make a column wider or narrower and the column width does not stay in line with where the mouse is.
When using row virtualization, the visible rows are miscalculated when the browser zoom is used (hold control with mouse scroll).
The result of the miscalculation is that the first row at some point is not visible.
To reproduce the problem you should scroll to the top of the grid and see the first row.
Then start to zoom down and see how the first row is displaced.
Seems like only 50%, 100%, 150%, 200% (multiply of 50%) are calculated correctly in row virtualization
<AdminEdit>
You can reproduce the bug with the attached code.
It's browser-specific and is appearing only in chromium browsers.
It could be related to the way browsers calculate the size of the elements when zooming.
Currently, there is an open bug in the chromium: https://bugs.chromium.org/p/chromium/issues/detail?id=1048147&q=component%3AUI%3EBrowser%3EZoom%20height&can=2
</AdminEdit>
Hi !
How can i hide some columns on small device ?
Telerik.Blazor.Components.GridColumn.Class does not exist ?
Regards,
--------
ADMIN EDIT
This will be done through the Visible parameter of the column. You can bind it to a flag that hides the column for the desired scenarios (resolution, user settings, etc.). The new feature we provide to facilitate this will be a MediaQuery component that lets you have an easy flag in the C# code based on the media query for the desired resolution. There will be a demo how to use it with the grid columns when the 2.23.0 release is live. With this approach you will still use a CSS media query, but this will give you more flexibility to use it in more functionality than just the grid columns, and will avoid adding extra properties to the column.
--------
I need the ability to show or hide command buttons based on a row's property.
I am aware that I can just cancel commands, but in my opinion, a command button should not even be shown when the command can't or shouldn't be executed on a row.
Unfortunately, I was unable to find a way to access the "context" (the instance) in a TelerikGridCommandColumn the same way you're able to in a normal TelerikGridColumn.
---
ADMIN EDIT
You can Vote for and Follow this request for a follow up on providing the model as context to the command column: https://feedback.telerik.com/blazor/1461283-pass-the-model-context-to-command-button. At the moment, conditional command buttons are possible in a "normal" column through the grid state, the page above shows an example.
---
Like this (example taken from "https://demos.telerik.com/aspnet-mvc/grid/editing-custom-validation"):