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.
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"):
---
ADMIN EDIT
This is not a bug, but intended behavior. As web grid is not Excel. You can find more details in the original thread opened by Aditya with this question: https://www.telerik.com/forums/grid-checkboxlist-filter---auto-select-all-upon-entering-filter-criteria
---
This is related to Grid CheckBoxList Filter - https://demos.telerik.com/blazor-ui/grid/filter-checkboxlist
In Microsoft Excel, when we start typing in the Search box, the matching entries along with "Select All" are already checked. So upon typing the required criteria, the user just needs to select Ok button to view the filtered results.
In the case of Blazor Grid, the matching entries and Select All option are not checked automatically. So the user would need to do an additional action of checking "Select All". Please refer to the attached screenshots.
Is there a way to have "Select All" auto checked upon typing the filter criteria?
asynct Task ContextMenuClick(ContextMenuItem) {
var state = Grid.GetState(); state.FilterDescriptors.Add(new FilterDescriptor { Member = "Created", Operator = FilterOperator.IsEqualTo, Value = "Administrator" }); await Grid.SetState(state);
}
How it should look like:
Data load in background thread with selected row on UI crashing application when there is selection
1. Select a row
2. Click the button
3. check the server console and/or try to do something else
@using System.Collections.ObjectModel
<TelerikGrid @ref="@Grid"
Data="@GridData"
SelectionMode="@GridSelectionMode.Single"
OnRowClick="@OnRowClickHandler"
@bind-SelectedItems="@SelectedData"
EditMode="@GridEditMode.Incell"
Resizable="true"
Width="100%"
Height="200px"
Reorderable="true"
Groupable="false"
ShowColumnMenu="false"
FilterMode="@GridFilterMode.None"
Sortable="true">
<GridColumns>
<GridColumn Field=@nameof(ViewModel.Name) Editable="false">
</GridColumn>
</GridColumns>
</TelerikGrid>
<TelerikButton OnClick="RefreshButtonClick">Refresh</TelerikButton>
@code {
private const int RowHeight = 40;
private const string Height = "700px";
private const int PageSize = 20;
private ObservableCollection<ViewModel> GridData { get; set; } = new ObservableCollection<ViewModel>();
private IEnumerable<ViewModel> SelectedData { get; set; } = Enumerable.Empty<ViewModel>();
private TelerikGrid<ViewModel> Grid { get; set; }
protected override async Task OnInitializedAsync()
{
await LoadData();
await base.OnInitializedAsync();
}
async Task LoadData()
{
var data = Enumerable.Range(1, 5).Select(x => new ViewModel { Name = $"name {x}" });
GridData = new ObservableCollection<ViewModel>(data);
}
private async Task RefreshButtonClick()
{
Console.WriteLine("reload data");
// spawn a new thread, a Timer will do as well
await Task.Run(() =>
{
IEnumerable<ViewModel> data = new List<ViewModel> { new ViewModel { Name = "1" }, new ViewModel { Name = "2" }, new ViewModel { Name = "3" } };
this.GridData.Clear();
this.GridData.AddRange(data);
});
}
private void OnRowClickHandler(GridRowClickEventArgs args)
{
Console.WriteLine("click to select");
ViewModel viewModel = (ViewModel)args.Item;
//this is here because of theincell editing, not relevant to the issue
SelectedData = new List<ViewModel>() { viewModel };
}
public class ViewModel
{
public ViewModel()
{
}
private string name;
public string Name
{
get
{
return name;
}
set
{
name = value;
}
}
}
}