If I'm filtering a column containing enum values I expect a dropdown of available values to choose from (as it is the case when filtering a grid using telerik UI for .net core).
Unfortunately with Blazor Grids the filter for Enums displays a numberbox. This is not usable since the user does not know the IDs for the enum values.
Please let me know how I can get the filter to let the user choose from the available enum values!
It will be useful if the grid column state contains the Field parameter if such is specified.
This will make easier mapping the ColumnState entity to the actual column.
----
It would be nice if I could specify an item in the inline editor (or any other for that matter) to get focus when the editor is activated.
Thanks,
Kenny
===
Telerik edit:
A possible workaround is:
The example below also shows how to enter Grid edit mode programmatically and how to focus a specific field when editing with OnRowClick.
<TelerikGrid @ref="@GridInlineRef"
Data="@GridInlineData"
EditMode="@GridEditMode.Inline"
OnAdd="@OnGridAddEdit"
OnEdit="@OnGridAddEdit"
OnCreate="@OnGridInlineCreate"
OnUpdate="@OnGridInlineUpdate"
OnRowClick="@OnGridRowClick">
<GridToolBarTemplate>
<GridCommandButton Command="Add">Add</GridCommandButton>
</GridToolBarTemplate>
<GridColumns>
<GridColumn Field="@nameof(Product.Name)">
<EditorTemplate>
@{ var editItem = (Product)context; }
<TelerikTextBox @ref="@NameTextBoxRef" @bind-Value="@editItem.Name" DebounceDelay="0" />
</EditorTemplate>
</GridColumn>
<GridColumn Field="@nameof(Product.Price)">
<EditorTemplate>
@{ var editItem = (Product)context; }
<TelerikNumericTextBox @ref="@PriceNumericTextBoxRef" @bind-Value="@editItem.Price" DebounceDelay="0" />
</EditorTemplate>
</GridColumn>
<GridColumn Field="@nameof(Product.Stock)">
<EditorTemplate>
@{ var editItem = (Product)context; }
<TelerikNumericTextBox @ref="@StockNumericTextBoxRef" @bind-Value="@editItem.Stock" DebounceDelay="0" />
</EditorTemplate>
</GridColumn>
<GridCommandColumn>
<GridCommandButton Command="Edit">Edit</GridCommandButton>
<GridCommandButton Command="Save" ShowInEdit="true">Update</GridCommandButton>
<GridCommandButton Command="Cancel" ShowInEdit="true">Cancel</GridCommandButton>
</GridCommandColumn>
</GridColumns>
</TelerikGrid>
@code {
private TelerikGrid<Product>? GridInlineRef { get; set; }
private TelerikTextBox? NameTextBoxRef { get; set; }
private TelerikNumericTextBox<decimal?>? PriceNumericTextBoxRef { get; set; }
private TelerikNumericTextBox<int>? StockNumericTextBoxRef { get; set; }
private string EditFieldToFocus { get; set; } = string.Empty;
private List<Product> GridInlineData { get; set; } = new List<Product>();
private void OnGridAddEdit(GridCommandEventArgs args)
{
EditFieldToFocus = nameof(Product.Stock);
}
private async Task OnGridRowClick(GridRowClickEventArgs args)
{
EditFieldToFocus = args.Field;
var itemToEdit = (Product)args.Item;
args.ShouldRender = true;
if (GridInlineRef != null)
{
var gridState = GridInlineRef.GetState();
gridState.InsertedItem = null!;
gridState.OriginalEditItem = itemToEdit;
gridState.EditItem = itemToEdit.Clone();
await GridInlineRef.SetStateAsync(gridState);
}
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (!string.IsNullOrEmpty(EditFieldToFocus))
{
await Task.Delay(200);
switch (EditFieldToFocus)
{
case nameof(Product.Name):
if (NameTextBoxRef != null)
{
await NameTextBoxRef.FocusAsync();
}
break;
case nameof(Product.Price):
if (PriceNumericTextBoxRef != null)
{
await PriceNumericTextBoxRef.FocusAsync();
}
break;
case nameof(Product.Stock):
if (StockNumericTextBoxRef != null)
{
await StockNumericTextBoxRef.FocusAsync();
}
break;
default:
break;
}
EditFieldToFocus = string.Empty;
}
}
#region Programmatic Inline Editing
private async Task InlineAdd()
{
var gridState = GridInlineRef!.GetState();
gridState.InsertedItem = new Product();
gridState.InsertedItem.Name = "New value";
gridState.OriginalEditItem = null!;
gridState.EditItem = null!;
await GridInlineRef.SetStateAsync(gridState);
}
private async Task InlineEdit()
{
if (GridInlineData.Any())
{
var gridState = GridInlineRef!.GetState();
gridState.InsertedItem = null!;
gridState.OriginalEditItem = GridInlineData.First();
gridState.EditItem = GridInlineData.First().Clone();
gridState.EditItem.Name = "Updated inline value";
EditFieldToFocus = nameof(Product.Price);
await GridInlineRef.SetStateAsync(gridState);
}
}
private async Task InlineCancel()
{
var gridState = GridInlineRef!.GetState();
gridState.InsertedItem = null!;
gridState.OriginalEditItem = null!;
gridState.EditItem = null!;
await GridInlineRef.SetStateAsync(gridState);
}
private async Task InlineUpdate()
{
var gridState = GridInlineRef!.GetState();
if (gridState.EditItem != null)
{
OnGridInlineUpdate(new GridCommandEventArgs()
{
Item = gridState.EditItem
});
}
else if (gridState.InsertedItem != null)
{
OnGridInlineCreate(new GridCommandEventArgs()
{
Item = gridState.InsertedItem
});
}
gridState.InsertedItem = null!;
gridState.OriginalEditItem = null!;
gridState.EditItem = null!;
await GridInlineRef.SetStateAsync(gridState);
}
#endregion Programmatic Inline Editing
#region Grid Inline Editing Handlers
private void OnGridInlineUpdate(GridCommandEventArgs args)
{
var updatedItem = (Product)args.Item;
var index = GridInlineData.FindIndex(i => i.Id == updatedItem.Id);
if (index != -1)
{
GridInlineData[index] = updatedItem;
}
}
private void OnGridInlineCreate(GridCommandEventArgs args)
{
var createdItem = (Product)args.Item;
createdItem.Id = Guid.NewGuid();
GridInlineData.Insert(0, createdItem);
}
#endregion Grid Inline Editing Handlers
#region Data Generation and Model
protected override void OnInitialized()
{
for (int i = 1; i <= 3; i++)
{
GridInlineData.Add(new Product()
{
Id = Guid.NewGuid(),
Name = $"Product {i}",
Price = Random.Shared.Next(1, 100) * 1.23m,
Stock = (short)Random.Shared.Next(0, 1000)
});
}
}
public class Product
{
public Guid Id { get; set; }
public string Name { get; set; } = string.Empty;
public decimal? Price { get; set; }
public int Stock { get; set; }
public DateTime ReleaseDate { get; set; }
public bool InProduction { get; set; }
public Product Clone()
{
return new Product()
{
Id = Id,
Name = Name,
Price = Price,
Stock = Stock,
ReleaseDate = ReleaseDate,
InProduction = InProduction
};
}
}
#endregion Data Generation and Model
}
Because the shape of our business objects is not something your grid is easily bound to, we are attempting to generate ExpandoOjbects on the fly and bind to them. In the picture below you can see that Id is not binding correctly. Name is showing up because a column Template was used and that seems to work fine. However, when attempting to use an EditorTemplate we get the RuntimeBinderException shown below.
@page "/dynamic-vendor"
I would like to be able to customize the title in Editing Popup.
<AdminEdit>
We plan to expose the following customization options:
</AdminEdit>
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.
With a pageable grid after scrolling down the first page and then paging, the next page should be scrolled to the top - but it is not.
Is there a way to scroll up by code until this is fixed ???
----
ADMIN EDIT
A sample solution is attached to the end of this post that shows how you can achieve this.
----
Based on row values, I want to style the entire row.
It could perhaps be exposed through something like a "<RowDescriptor>" element inside the <GridColumns>
//ADMIN EDIT: We plan to extend this item so that it includes not only row styling, but also the ability to add CSS classes to the cells, without having to use the Template.
ADMIN EDIT: Please review this thread and add your comments so we can get the community feedback on this. We have attached to this opener post a small sample that shows how to achieve this with a few lines of code, and a short video of that behavior.
Hello Team;
The Grid Popup is a great feature, but my understanding is that the Popup form ONLY shows properties that are assigned as columns to the Grid.
If true, this poses some restrictions for us. Many times we might show ONLY small # of columns in Grid, however the form requires MORE properties during ADD or UPDATE.
Is it possible that we can use two ViewModels, one for the Grid columns with less properties and one with more properties for ADD & UPDATE?
Note: If this FR is considered, perhaps we can have separate ViewModel for Update and ADD, as sometimes, ADD might require more properties to be added than later be updated.
This feature will save a lot of time to build apps that have many tables and we have to create CRUD operations
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)
Hello everyone,
We created this Feature Request to gather feedback on the way the item expansion is working in the Grid state. Currently, you can expand any items with the ExpandedRows collection of int where you pass the indexes of the rows.
Another option, which we are thinking of, is to provide a Collection<Model>. This would let you pass models, instead of indexes and the Grid would automatically expand those items. This approach would make the need to preserve the indexes redundant, which you should currently do when Filtering/Sorting and Grouping the items in the Grid.
We would appreciate your feedback on both approaches and which you think will be easier to use.
When you change the data source of the grid, it must re-render the data again.
For example, when you use a custom edit form, you add/edit the data with your own code and not through the grid. This is useful, for example, when you only want to show a few columns in the grid, but the model has many more editable fields. Or, when you want a customized layout/behavior of the edit form.
Minimum repro:
@
using
Telerik.Blazor.Components.Grid
@
using
Telerik.Blazor.Components.Button
<TelerikButton OnClick=
"@ChangeProduct"
>Works: Change an existing product</TelerikButton>
<TelerikButton OnClick=
"@AddProduct"
>Does not work: Add a
new
product</TelerikButton>
<TelerikButton OnClick=
"@RemoveProduct"
>Does not work: Remove an existing product</TelerikButton>
<TelerikGrid Data=@GridData
Pageable=
"true"
>
<TelerikGridColumns>
<TelerikGridColumn Field=@nameof(Product.ProductName) Title=
"Product Name"
/>
<TelerikGridColumn Field=@nameof(Product.UnitPrice) Title=
"Unit Price"
>
</TelerikGridColumn>
</TelerikGridColumns>
</TelerikGrid>
@functions {
public
List<Product> GridData {
get
;
set
; }
void
AddProduct()
{
GridData.Insert(0,
new
Product()
{
ProductId = DateTime.Now.Millisecond,
ProductName =
"some product name"
,
UnitPrice = DateTime.Now.Second
});
//after updating the data, the grid should show the item at the top of the first page.
//at the moment, you need to page, for example, for the data to be updated
}
protected
void
ChangeProduct()
{
GridData.Where(p => p.ProductId == 2).First().ProductName =
"changed at "
+ DateTime.Now;
}
protected
void
RemoveProduct()
{
GridData.RemoveAt(4);
//after updating the data, the grid should remove the fourth item immediately
//at the moment, you need to page, for example, for the data to be updated
}
protected
override
void
OnInit()
{
GridData =
new
List<Product>();
for
(
int
i = 0; i < 25; i++)
{
GridData.Add(
new
Product()
{
ProductId = i,
ProductName =
"Product"
+ i.ToString(),
UnitPrice = (
decimal
)(i * 3.14),
});
}
}
public
class
Product
{
public
string
ProductName {
get
;
set
; }
public
decimal
UnitPrice {
get
;
set
; }
public
int
ProductId {
get
;
set
; }
}
}