Hi, I have plenty of pages that have same data grid and form feature, so I'm trying to build a generic component with reflection, I just need to pass my data to the generic component and I can get the pages.
I create a generic RadzenTemplateForm component called GenericForm, iterate a list and assign value to child component GenericField, GenericField can be RadzenDropDown, RadzenTextBox or RadzenNumeric(I customized some Attributes to decide which component it could be), for now I implement Value and ValueChanged, the component's value I mentioned above can showe and save back to form model successfully, but I can't figure out how to customize ValueExpression, without it the validation won't work.
When GenericField is triggered, there'll be error in FormComponent's SetParametersAsync method:
The provided expression contains a UnaryExpression which is not supported. FieldIdentifier only supports simple member accessors (fields, properties) of an object.'
Here is my GenericForm code:
@typeparam DataGridType where DataGridType : new()
@typeparam ServiceType
@typeparam ListItemType
<RadzenTemplateForm TItem="DataGridType" Data="Model" Submit=@Submit>
<RadzenFieldset>
@{
@for (int j = 0; j < genericFields.Count; j++)
{
<TTMField @bind-MyValue="genericFields[j].Value"
CustomValueExpression="@(() => genericFields[j].Value)"
ListData="genericFields[j].List" Attribute="genericFields[j].Attribute" />
}
}
<div class="row mt-3">
<div class="col-md-12">
<RadzenButton ButtonType="ButtonType.Submit" Text="Save"></RadzenButton>
</div>
</div>
</RadzenFieldset>
</RadzenTemplateForm>
@code {
[Parameter] public Dictionary<string, List<ListItemType>> DropDownLists { get; set; }
[Parameter] public EventCallback<DataGridType> SubmitCallback { get; set; }
[Parameter] public DataGridType Model { get; set; }
private List<TTMFieldModel<DataGridType, ListItemType>>? genericFields { get; set; }
private DataGridType dataGridModel { get; set; }
protected override async Task OnInitializedAsync()
{
genericFields = new List<TTMFieldModel<DataGridType, ListItemType>>();
dataGridModel = new DataGridType();
}
protected override async Task OnParametersSetAsync()
{
var attributes = typeof(DataGridType).GetProperties()
.Where(a => a.GetCustomAttribute<TTMModifyFormAttribute>() != null)
.ToDictionary(a => a.Name, a => a.GetCustomAttribute<TTMModifyFormAttribute>())
.OrderBy(a => a.Value?.Order)
.ToList();
genericFields = attributes.Select(a => new TTMFieldModel<DataGridType, ListItemType>
(
typeof(DataGridType).GetProperty(a.Key),
DropDownLists.TryGetValue(a.Key, out List<ListItemType> list) ? DropDownLists[a.Key] : null,
Model,
a.Value)
).ToList();
}
public async Task Submit()
{
try
{
foreach (var property in genericFields)
{
dataGridModel.GetType().GetProperty(property.PropertyInfo.Name).SetValue(dataGridModel, property?.Value);
}
await SubmitCallback.InvokeAsync(dataGridModel);
}
catch (Exception ex)
{
Console.WriteLine(ex);
throw;
}
}
}
Here is my GenericField code:
@typeparam FieldType
@typeparam ListItemType
<RadzenRequiredValidator Component="@modifyFormAttribute.DisplayName" Text="is required" Style="position: absolute" />
<div class="col-2">
@switch (modifyFormAttribute.InputType)
{
case TTMInputType.DropDown:
<RadzenDropDown Data="ListData" AllowClear="true" AllowVirtualization="true" Class="w-100" AllowFiltering="true"
TextProperty="Text" ValueProperty="Value"
Value="@MyValue" ValueChanged="@( async (FieldType v) => await ObjectValueChanged(v) )"
ValueExpression="@CustomValueExpression" Name="@modifyFormAttribute.DisplayName"
Disabled="modifyFormAttribute.DisabledInEditMode" Visible="modifyFormAttribute.VisibleInEditMode" />
break;
case TTMInputType.TextBox:
<RadzenTextBox Value="@MyValue?.ToString()" ValueChanged="@( async (string v) => await StringValueChanged(v) )"
ValueExpression="@( () => (string)(object)MyValue)"
Name="@modifyFormAttribute.DisplayName" Disabled="modifyFormAttribute.DisabledInEditMode" Visible="modifyFormAttribute.VisibleInEditMode" />
break;
default:
break;
}
</div>
@code {
[Parameter]
public FieldType MyValue { get; set; }
[Parameter]
public EventCallback<FieldType> MyValueChanged { get; set; }
[Parameter]
public Expression<Func<FieldType>> CustomValueExpression { get; set; }
[Parameter]
public List<ListItemType> ListData { get; set; }
[Parameter]
public ITTMDataGridAttribute modifyFormAttribute { get; set; }
async Task ObjectValueChanged(FieldType v)
{
MyValue = v;
if (MyValueChanged.HasDelegate)
{
await MyValueChanged.InvokeAsync(MyValue);
}
}
async Task StringValueChanged(string v)
{
MyValue = (FieldType)(object)v;
if (MyValueChanged.HasDelegate)
{
await MyValueChanged.InvokeAsync(MyValue);
}
}
}