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
The null type of operator can cause errors on the backend
*** Thread created by admin on customer behalf ***
On Hierarchy grids with Inline edit mode, I noticed that if a row is currently being edited and I try to expand the row to show the child grid an exception is thrown: "Error: System.InvalidOperationException: TelerikValidationComponent requires a cascading parameter of type EditContext. You can use Telerik.Blazor.Components.TelerikValidationTooltip`1[System.String] inside a EditForm or TelerikForm".
The problem stems from a validation tool inside the Grid EditorTemplate.
There is a change in the Grid behavior from version 4.6.0 to 5.0.0.
Consider the following REPL test page: https://blazorrepl.telerik.com/cSabmRPy306iPiXK18
Clicking on a Grid cell for editing will not open it for editing if there is already an open cell in edit mode on the same table row. The issue is reproduced more easily if the OnUpdate handler is empty or not defined at all.
This used to work in version 4.6.0.
When a user deletes a record that decreases the number of pages on that Grid, and then they try to edit or add a record, they get stuck in the OnCancel event handler, which gets called over and over in an endless loop.
The behavior can be seen:
Steps To Reproduce
https://blazorrepl.telerik.com/GokIvFva42BLx7yl37
1. Delete item 11. The Grid will move you to page 1.
2. Try to edit or add a new item. OnCancel will fire in a loop.
3. If OnAdd and OnEdit are not defined, the loop will occur in OnUpdate and OnCancel.
In the case of a sorted column, NVDA is not narrating the correct column name and narrating the incorrect Roles for the column headers.
In the case of an unsorted column, NVDA is narrating the column name twice and repeating the information.
I have a TelerikGrid with Reordarable enabled inside of a TelerikWindow. Reordering of the column works fine only the drop clue is not showing. I think this is because the z-index is incorrect.
Missing drop clue:
z-index of drop clue is 10000:
z-index of window is 10002:
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.
It seems that the issue appeared after upgrading to Telerik 5.0 from 4.6.
When using a Grid with a locked column and column virtualization enabled, focusing cell from that locked column breaks the layout on horizontal scrolling.
---
ADMIN EDIT
Attached to this post are a reproducible and a workaround - setting the state in OnAfterRenderAsync with a small delay, so the initial grid render happens, then it can re-render and take the filters into account.
---
In a Grid loaded with data made of ExpandoObject, set an aggregate GridAggregateType.Sum breaks the grouping feature while GridAggregateType Max, Min and Count work properly
Please find the attached project: in the grid on Index.razor grouping does not work, just comment Index.razor:43 to restore grouping feature
Exception message:
Error: System.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.
at System.Linq.Expressions.Expression.FindMethod(Type type, String methodName, Type[] typeArgs, Expression[] args, BindingFlags flags)
at System.Linq.Expressions.Expression.Call(Type type, String methodName, Type[] typeArguments, Expression[] arguments)
at Telerik.DataSource.Expressions.EnumerableSelectorAggregateFunctionExpressionBuilder.CreateMethodCallExpression(LambdaExpression memberSelectorExpression)
at Telerik.DataSource.Expressions.EnumerableSelectorAggregateFunctionExpressionBuilder.CreateAggregateExpression()
at Telerik.DataSource.EnumerableSelectorAggregateFunction.CreateAggregateExpression(Expression enumerableExpression, Boolean liftMemberAccessToNull)
at Telerik.DataSource.Expressions.GroupDescriptorExpressionBuilder.<ProjectionPropertyValueExpressions>b__39_0(AggregateFunction f)
at System.Linq.Enumerable.SelectIListIterator`2.ToList()
at Telerik.DataSource.Expressions.GroupDescriptorExpressionBuilder.CreateProjectionInitExpression()
at Telerik.DataSource.Expressions.GroupDescriptorExpressionBuilder.CreateAggregateFunctionsProjectionMemberBinding()
at Telerik.DataSource.Expressions.QueryableAggregatesExpressionBuilder.CreateMemberBindings()+MoveNext()
at System.Collections.Generic.LargeArrayBuilder`1.AddRange(IEnumerable`1 items)
at System.Collections.Generic.EnumerableHelpers.ToArray[T](IEnumerable`1 source)
at System.Linq.Enumerable.ToArray[TSource](IEnumerable`1 source)
at System.Dynamic.Utils.CollectionExtensions.ToReadOnly[T](IEnumerable`1 enumerable)
at System.Linq.Expressions.Expression.MemberInit(NewExpression newExpression, IEnumerable`1 bindings)
at Telerik.DataSource.Expressions.GroupDescriptorExpressionBuilder.CreateSelectBodyExpression()
at Telerik.DataSource.Expressions.GroupDescriptorExpressionBuilder.CreateSelectExpression()
at Telerik.DataSource.Expressions.GroupDescriptorExpressionBuilderBase.CreateQuery()
at Telerik.DataSource.Extensions.QueryableExtensions.Aggregate(IQueryable source, IEnumerable`1 aggregateFunctions)
at Telerik.DataSource.Extensions.QueryableExtensions.CreateDataSourceResult[TModel,TResult](IQueryable queryable, DataSourceRequest request, Func`2 selector)
at Telerik.DataSource.Extensions.QueryableExtensions.ToDataSourceResult(IQueryable queryable, DataSourceRequest request)
at Telerik.DataSource.Extensions.QueryableExtensions.ToDataSourceResult(IEnumerable enumerable, DataSourceRequest request)
at Telerik.Blazor.Data.TelerikDataSourceBase.ProcessData(IEnumerable data)
at Telerik.Blazor.Components.Common.DataBoundComponent`1.ProcessDataInternal()
at Telerik.Blazor.Components.Common.DataBoundComponent`1.ProcessDataAsync()
at Telerik.Blazor.Components.TelerikGrid`1.DataBoundProcessData()
at Telerik.Blazor.Components.TelerikGrid`1.ProcessDataAsync()
at Telerik.Blazor.Components.TelerikGrid`1.ApplyFiltersAsync()
at Telerik.Blazor.Components.TelerikGrid`1.OnFilterChange(FilterDescriptorBase filter)
at Telerik.Blazor.Components.Common.Filters.TelerikFilterHeader`1.Filter(FilterDescriptor filterDescriptor)
at Telerik.Blazor.Components.Common.Filters.TelerikFilterHeader`1.OnValueChanged(Object newValue)
at System.Threading.Tasks.Task.<>c.<ThrowAsync>b__139_0(Object state)
at Microsoft.AspNetCore.Components.Rendering.RendererSynchronizationContext.ExecuteSynchronously(TaskCompletionSource`1 completion, SendOrPostCallback d, Object state)
at Microsoft.AspNetCore.Components.Rendering.RendererSynchronizationContext.<>c.<.cctor>b__23_0(Object state)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
--- End of stack trace from previous location where exception was thrown ---
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at Microsoft.AspNetCore.Components.Rendering.RendererSynchronizationContext.ExecuteBackground(WorkItem item)
Greetings,
When using single selection mode, a row can be selected either by clicking the checkbox or by clicking on the rest of the row. There is no difference at all. Now, let's say I have a grid with multiple selection mode enabled, e.g.:
<TelerikGrid Data="listOfFoos" SelectionMode="GridSelectionMode.Multiple">
<GridColumns>
<GridCheckboxColumn SelectAll="true" SelectAllMode="GridSelectAllMode.All" />
<GridColumn Field="@nameof(Foo.Name)" Title="Name" />
</GridColumns>
</TelerikGrid>
public class Foo {
public string Name { get; set; }
}
public List<Foo> listOfFoos = [ new Foo{Name="First"}, new Foo{Name="Second"}, new Foo{Name="Third"} ];
When we click an unselected row, the behavior varies depending on where we click exactly:
This notably makes multiple selection impossible if we click on the row but not on the checkbox and gives the impression we are using single selection mode. It is especially strange if we consider the existence of the CheckBoxOnlySelection parameter of <GridCheckboxColumn> whose name suggests we can select using the rest of the row by default.
Triggering Edit in a Grid bound to a DataTable or ExpandoObject and with edit mode set to Popup throws an unhandled exception. This started to occur in 4.1.0 - the same code runs fine on 4.0.1
To reproduce:
When the Grid is databound via OnRead event, the initially displayed aggregates are wrong and take into account the first page only.
A possible workaround is to bind the Grid in OnAfterRenderAsync -
@using Telerik.DataSource
@using Telerik.DataSource.Extensions
<TelerikGrid @ref="@GridRef"
OnRead="@OnGridRead"
TItem="@Product"
Pageable="true">
<GridAggregates>
<GridAggregate Field="@nameof(Product.Name)" Aggregate="@GridAggregateType.Count" FieldType="@(typeof(System.String))" />
</GridAggregates>
<GridColumns>
<GridColumn Field="@nameof(Product.Name)" Title="Product Name">
<FooterTemplate>
@context.Count
</FooterTemplate>
</GridColumn>
<GridColumn Field="@nameof(Product.Price)" />
<GridColumn Field="@nameof(Product.ReleaseDate)" Title="Release Date" />
<GridColumn Field="@nameof(Product.Active)" />
</GridColumns>
</TelerikGrid>
@code {
TelerikGrid<Product> GridRef { get; set; }
List<Product> GridData { get; set; }
bool ShouldBindGrid { get; set; }
async Task OnGridRead(GridReadEventArgs args)
{
if (!ShouldBindGrid)
{
return;
}
await Task.Delay(200); // simulate network delay
DataSourceResult result = GridData.ToDataSourceResult(args.Request);
args.Data = result.Data;
args.Total = result.Total;
args.AggregateResults = result.AggregateResults;
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
// workaround for initial Grid aggregates
ShouldBindGrid = true;
GridRef.Rebind();
StateHasChanged();
}
}
protected override void OnInitialized()
{
GridData = new List<Product>();
var rnd = new Random();
for (int i = 1; i <= 50; i++)
{
GridData.Add(new Product()
{
Id = i,
Name = "Product " + i.ToString(),
Price = (decimal)rnd.Next(1, 100),
ReleaseDate = DateTime.Now.AddDays(-rnd.Next(60, 1000)),
Active = i % 3 == 0
});
}
}
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
public DateTime ReleaseDate { get; set; }
public bool Active { get; set; }
}
}
In this example. Select a cell in column C. Then double click on another cell in column C. The unhandled exception will show up and a null reference exception is logged in the console.
https://blazorrepl.telerik.com/QHuSQBlj170zO4u130
The exception message is:
System.InvalidOperationException: TelerikValidationComponent requires a cascading parameter of type EditContext. You can use Telerik.Blazor.Components.TelerikValidationTooltip`1[System.String] inside a EditForm or TelerikForm.
I have a Grid bound to ObservableCollection and I try to add a new record, the Grid stays in edit mode even if I click the Save button.
Workaround:
@using System.Collections.ObjectModel
@using Telerik.FontIcons
<TelerikGrid Data="@Cards"
Height="800px"
FilterMode="GridFilterMode.FilterRow"
EditMode="GridEditMode.Inline"
OnCreate="NewCard"
OnUpdate="UpdateCard"
OnDelete="DeleteCard"
ConfirmDelete="true"
@ref="@GridReference">
<GridToolBarTemplate>
<GridCommandButton Command="Add" Icon="@FontIcon.Plus">New</GridCommandButton>
</GridToolBarTemplate>
<GridColumns>
<GridColumn Field="@nameof(AdminCard.SerialNumber)" Title="Serial number" />
<GridColumn Field="@nameof(AdminCard.DisplayNumber)" Title="Display number" />
<GridColumn Field="@nameof(AdminCard.Name)" />
<GridCommandColumn Width="140px">
<GridCommandButton Command="Save" Icon="@FontIcon.Save" ShowInEdit="true" />
<GridCommandButton Command="Edit" Icon="@FontIcon.Pencil" />
<GridCommandButton Command="Delete" Icon="@FontIcon.Trash" />
<GridCommandButton Command="Cancel" Icon="@FontIcon.Cancel" ShowInEdit="true" />
</GridCommandColumn>
</GridColumns>
</TelerikGrid>
@code {
TelerikGrid<AdminCard> GridReference { get; set; }
public ObservableCollection<AdminCard> Cards { get; set; } = new();
protected override async Task OnInitializedAsync() =>
Cards = new ObservableCollection<AdminCard>{
new AdminCard{SerialNumber="123", DisplayNumber="1234 1234", Name="Jim"}
};
private async Task NewCard(GridCommandEventArgs args)
{
AdminCard card = (AdminCard)args.Item;
Cards.Add(card);
//apply this after saving the new record to the database
var gridState = GridReference.GetState();
gridState.InsertedItem = null;
gridState.OriginalEditItem = null;
await GridReference.SetStateAsync(gridState);
}
private async Task UpdateCard(GridCommandEventArgs args)
{
AdminCard fromGrid = (AdminCard)args.Item;
AdminCard existing = Cards.Single(c => c.Id == fromGrid.Id);
existing.SerialNumber = fromGrid.SerialNumber;
existing.DisplayNumber = fromGrid.DisplayNumber;
existing.Name = fromGrid.Name;
}
private async Task DeleteCard(GridCommandEventArgs args)
{
AdminCard card = (AdminCard)args.Item;
Cards.Remove(card);
}
public class AdminCard
{
public string Id { get; set; } = Guid.NewGuid().ToString();
public string SerialNumber { get; set; } = "";
public string DisplayNumber { get; set; } = "";
public string Name { get; set; } = "";
}
}
Consider this test page: https://blazorrepl.telerik.com/cSEQOTQY53LRL4je36
This happens only when using a column menu - ShowColumnMenu="true".
===
A possible workaround is to use a FilterButtonsTemplate and clear the filters programmatically if the filter descriptor is empty:
@using Telerik.DataSource
@using Telerik.DataSource.Extensions
<TelerikGrid TItem="@Employee"
OnRead="@OnReadHandler"
Pageable="true"
FilterMode="@GridFilterMode.FilterMenu"
FilterMenuType="@FilterMenuType.CheckBoxList"
ShowColumnMenu="true"
Height="400px">
<GridColumns>
<GridColumn Field="@(nameof(Employee.EmployeeId))" Filterable="false" />
<GridColumn Field="@nameof(Employee.Name)">
<FilterMenuTemplate Context="context">
<TelerikCheckBoxListFilter Data="@NameOptions"
Field="@(nameof(NameFilterOption.Name))"
@bind-FilterDescriptor="@context.FilterDescriptor">
</TelerikCheckBoxListFilter>
</FilterMenuTemplate>
<FilterMenuButtonsTemplate Context="filterContext">
<TelerikButton OnClick="@( async () => await ApplyFilterAsync(filterContext) )"
ThemeColor="primary">Filter</TelerikButton>
<TelerikButton OnClick="@( async () => await ClearFilterAsync(filterContext) )">Clear</TelerikButton>
</FilterMenuButtonsTemplate>
</GridColumn>
<GridColumn Field="@nameof(Employee.Team)" Title="Team">
<FilterMenuTemplate Context="context">
<TelerikCheckBoxListFilter Data="@TeamsList"
Field="@(nameof(TeamNameFilterOption.Team))"
@bind-FilterDescriptor="@context.FilterDescriptor">
</TelerikCheckBoxListFilter>
</FilterMenuTemplate>
</GridColumn>
<GridColumn Field="@nameof(Employee.IsOnLeave)" Title="On Vacation" />
</GridColumns>
</TelerikGrid>
@code {
List<Employee> AllGridData { get; set; }
#region custom-filter-data
List<TeamNameFilterOption> TeamsList { get; set; }
List<NameFilterOption> NameOptions { get; set; }
private async Task ApplyFilterAsync(FilterMenuTemplateContext filterContext)
{
var hasFilters = filterContext.FilterDescriptor.FilterDescriptors.OfType<FilterDescriptor>().Any(x => !string.IsNullOrEmpty(x.Value.ToString()));
if (hasFilters)
{
await filterContext.FilterAsync();
}
else
{
await filterContext.ClearFilterAsync();
}
}
private async Task ClearFilterAsync(FilterMenuTemplateContext filterContext)
{
await filterContext.ClearFilterAsync();
}
//obtain filter lists data from the data source to show all options
async Task GetTeamOptions()
{
if (TeamsList == null) // sample of caching since we always want all distinct options,
//but we don't want to make unnecessary requests
{
TeamsList = await GetNamesFromService();
}
}
async Task<List<TeamNameFilterOption>> GetNamesFromService()
{
await Task.Delay(500);// simulate a real service delay
// this is just one example of getting distinct values from the full data source
// in a real case you'd probably call your data service here instead
// or apply further logic (such as tie the returned data to the data the grid will have according to your business logic)
List<TeamNameFilterOption> data = AllGridData.OrderBy(z => z.Team).Select(z => z.Team).
Distinct().Select(t => new TeamNameFilterOption { Team = t }).ToList();
return await Task.FromResult(data);
}
async Task GetNameOptions()
{
if (NameOptions == null)
{
NameOptions = await GetNameOptionsFromService();
}
}
async Task<List<NameFilterOption>> GetNameOptionsFromService()
{
await Task.Delay(500);// simulate a real service delay
List<NameFilterOption> data = AllGridData.OrderBy(z => z.Name).Select(z => z.Name).
Distinct().Select(n => new NameFilterOption { Name = n }).ToList();
return await Task.FromResult(data);
}
#endregion custom-filter-data
async Task OnReadHandler(GridReadEventArgs args)
{
//typical data retrieval for the grid
var filteredData = await AllGridData.ToDataSourceResultAsync(args.Request);
args.Data = filteredData.Data as IEnumerable<Employee>;
args.Total = filteredData.Total;
}
protected override async Task OnInitializedAsync()
{
AllGridData = new List<Employee>();
var rand = new Random();
for (int i = 1; i <= 15; i++)
{
AllGridData.Add(new Employee()
{
EmployeeId = i,
Name = "Employee " + i.ToString(),
Team = "Team " + i % 3,
IsOnLeave = i % 2 == 0
});
}
await GetTeamOptions();
await GetNameOptions();
}
public class Employee
{
public int EmployeeId { get; set; }
public string Name { get; set; }
public string Team { get; set; }
public bool IsOnLeave { get; set; }
}
// in this sample we use simplified models to fetch less data from the service
// instead of using the full Employee model that has many fields we do not need for the filters
public class TeamNameFilterOption
{
public string Team { get; set; }
}
public class NameFilterOption
{
public string Name { get; set; }
}
}
The filter list item menu defined as follows do not have accessible name. Please provide work around or fix that I can implement.
<TelerikGrid Data="@ViewModel.RouterAndDataLossInformation" TItem="TrafficLossSummary"
Pageable="true"
Sortable="true"
Groupable="false"
FilterMode="Telerik.Blazor.GridFilterMode.FilterRow"
Resizable="true"
Reorderable="true"
Height = "100%">