Hi, in a my application I save the filters to db, and I permit the user to edit this.
The ValueTemplate (in FilterField component) works perfectly on insert, but not when I update the value of an existing FilterDescriptor (the CompositeFilterDescriptor is not updated).
I've gone deeper, and I think that missing a call to FilterChanged method in the TelerikFilter (from the child).
When the TelerikFilter render a FilterField this set the FilterChanged method that is used any time that you change something in the filter, but with a custom component this call missing.
I think that you must expose the FilterChanged (that now is private) or in TelerikFilter, or better in in context (FilterFieldValueTemplateContext) that you use in the FilterField.ValueTemplate, to force the update in the parent component.
To test this, you can use your code in https://blazorrepl.telerik.com/wIuhlcYV35fUROFX47 and add a new FilterDescriptor in the OnInitialized method, or you can test it with the code below (based on your own).
@using Telerik.Blazor.Components
@using Telerik.DataSource
@using Telerik.DataSource.Extensions
@{
var firstFilter = (Telerik.DataSource.FilterDescriptor?)FilterValue?.FilterDescriptors.FirstOrDefault();
if (firstFilter is not null)
{
<div>
firstFilter value: @firstFilter.Value
</div>
}
}
<TelerikFilter Value="@FilterValue" ValueChanged="@OnValueChanged">
<FilterFields>
@foreach (var f in FilterFields)
{
if (nameof(Food.Price) == f.Name)
{
<FilterField Name="@f.Name" Type="@f.Type" Label="@f.Label" >
<ValueTemplate>
<TelerikNumericTextBox Value="@((decimal?)context.FilterDescriptor.Value)"
ValueChanged="@( (decimal? value) => NumericValueChanged(context.FilterDescriptor, value) )">
</TelerikNumericTextBox>
</ValueTemplate>
</FilterField>
}
else
{
<FilterField Name="@f.Name" Type="@f.Type" Label="@f.Label" />
}
}
</FilterFields>
</TelerikFilter>
<TelerikGrid Data="@GridData"
Height="400px">
<GridColumns>
<GridColumn Field="@(nameof(Food.Id))" />
<GridColumn Field="@(nameof(Food.Name))" />
<GridColumn Field="@(nameof(Food.Price))" />
<GridColumn Field="@(nameof(Food.IsAvailable))" />
</GridColumns>
</TelerikGrid>
@code {
private List<Food> GridData { get; set; } = new();
private List<Food> InitialData { get; set; } = new();
private CompositeFilterDescriptor FilterValue { get; set; } = new();
private List<string> Suggestions { get; set; } = new() { "Pasta", "Burger", "Pizza", "Kebab", "Steak", "Ice Cream" };
private void OnFilterValueChanged(FilterDescriptor fd, string value)
{
fd.Value = value;
ProcessGridData();
}
private void NumericValueChanged(FilterDescriptor fd, decimal? value)
{
fd.Value = value;
var a = FilterValue;
ProcessGridData();
}
private void OnValueChanged(CompositeFilterDescriptor value)
{
FilterValue = value;
ProcessGridData();
}
private void ProcessGridData()
{
CompositeFilterDescriptor filter = FilterValue;
var dataSourceRequest = new DataSourceRequest { Filters = new List<IFilterDescriptor> { filter } };
var dataSourceResult = InitialData.ToDataSourceResult(dataSourceRequest);
GridData = dataSourceResult.Data.Cast<Food>().ToList();
}
protected override void OnInitialized()
{
FilterValue.FilterDescriptors.Add(new FilterDescriptor { Member = nameof(Food.Price), Operator = FilterOperator.IsEqualTo, Value = Convert.ToDecimal(0), MemberType = typeof(decimal) });
LoadData();
base.OnInitialized();
}
private void LoadData()
{
InitialData = new List<Food>
{
new Food { Id = 1, Name = "Pasta", Price = 13.99m, IsAvailable = true},
new Food { Id = 2, Name = "Burger", Price = 11.99m, IsAvailable = false},
new Food { Id = 3, Name = "Pizza", Price = 16.99m, IsAvailable = true},
new Food { Id = 4, Name = "Kebab", Price = 9.99m, IsAvailable = true },
new Food { Id = 5, Name = "Steak", Price = 22.99m, IsAvailable = false },
new Food { Id = 6, Name = "Salad", Price = 6.99m, IsAvailable = true},
new Food { Id = 6, Name = "Ice Cream", Price = 4.99m, IsAvailable = true }
};
ProcessGridData();
}
public class Food
{
public int Id { get; set; }
public string Name { get; set; } = string.Empty;
public decimal Price { get; set; }
public bool IsAvailable { get; set; }
}
public List<MyFilterField> FilterFields = new List<MyFilterField>
{
new MyFilterField { Name = nameof(Food.Price), Type = typeof(double?), Label = "Price" },
new MyFilterField { Name = nameof(Food.Id), Type = typeof(int), Label = "Id" },
new MyFilterField { Name = nameof(Food.Name), Type = typeof(string), Label = "Name" },
new MyFilterField { Name = nameof(Food.IsAvailable), Type = typeof(bool), Label = "Is Available" }
};
public class MyFilterField
{
required public string Name { get; set; }
required public Type Type { get; set; }
required public string Label { get; set; }
}
}
I would like to request an ability to add a dropdown of available values so that a user could select one or more items on the value side.
For Example:
User selects "Room Type" for Filter Name and is presented with "Queen", "King", "Double". The user would then be able to select one or more of those values. This would replace a multitude of "OR" statements, in additional; it could make finding things easier by knowing what values I can search for.
Possibly apply a "Template" for the value of a Filter Name. Purely as a concept:
<TelerikFilter Value="@Value" ValueChanged="@OnValueChanged">
<FilterFields>
<FilterField Name="@nameof(OrderDetailDto.OrderId)" Type="typeof(int)" Label="Id" />
<FilterField Name="@nameof(OrderDetailDto.Quantity)" Type="typeof(short)" />
<FilterField Name="@nameof(OrderDetailDto.OrderFreight)" Type="@typeof(decimal)" Label="Freight" />
<FilterField Name="@nameof(OrderDetailDto.OrderShipCountry)" Type="typeof(string)" Label="Country">
<FilterValueTemplate>{{Some Template Here}}</FilterValueTemplate>
</FilterField>
<FilterField Name="@nameof(OrderDetailDto.OrderShipName)" Type="typeof(List<string>)" Label="Ship to">
<FilterValueTemplate>{{Some Template Here}}</FilterValueTemplate>
</FilterField>
<FilterField Name="@nameof(OrderDetailDto.OrderShipAddress)" Type="typeof(string)" Label="Ship Address" />
</FilterFields>
</TelerikFilter>