如何根据属性类型在Blazor中动态创建输入表单



我正试图遍历一个模型,并为模型中的每个属性创建一个表单组件。我继续遇到各种各样的错误,需要一些帮助。这是相关代码:

表格。剃刀

<div class="form-section">
@foreach (var property in DataModel.GetType().GetProperties())
{
var propertyString = $"DataModel.{property.Name.ToString()}";
@if (property.PropertyType == typeof(DateTime))
{
<InputDate id=@"property.Name" @bind-Value="@propertyString">
}
else if (property.PropertyType == typeof(int))
{
<InputNumber id=@"property.Name" @bind-Value="@propertyString">
}
else
{
<InputText id=@"property.Name" @bind-Value="@propertyString">
}
}
</div>

DataModel.cs

public class DataModel
{
public DateTime SelectedData { get; set; }
public int SelectedNumber { get; set; }
public decimal SelectDecimal { get; set; }
}

显然,我已经大大简化了代码。上面显示的forms.razor中的div嵌套在EditForm元素中。这是在类DataModel的属性中循环,如果类型是DateTime,它应该呈现InputDate窗体或InputNumber窗体等。如果只有3个属性,我只会硬编码它们,但在实际应用程序中,我有超过100个DateTime、int或decimal类型的属性,所以我正在寻找一个更具编程性的解决方案。我的目标是让它检查类中每个属性的类型,然后正确地呈现与该数据类型相关联的适当表单,绑定到正确的属性。目前,我只能获得要呈现的InputDate表单,但当我在表单中输入日期时,我会得到以下异常:

错误:系统。InvalidOperationException:类型"System。字符串"不是支持的日期类型

我猜这是因为我在InputDate组件的@bind-Value参数中传递了一个属性名称字符串。不过,我无法将实际引用传递给该属性。有人能帮我详细了解一下如何解决这个问题吗?

我终于弄明白了这一点。最后,我在onchange属性中使用了一个回调来设置循环中当前属性的值。

<div class="form-section">
@foreach (var property in DataModel.GetType().GetProperties())
{
var propertyString = $"DataModel.{property.Name.ToString()}";
@if (property.PropertyType == typeof(DateTime))
{
<input type="date" id="@property.Name" @onchange="@(e => property.SetValue(dataModel, DateTime.Parse(e.Value.ToString())))" />
}
else if (property.PropertyType == typeof(int))
{
<input type="number" id="@property.Name" @onchange="@(e => property.SetValue(dataModel, Int32.Parse(e.Value.ToString())))" />
}
else
{
<input type="number" id="@property.Name" @onchange="@(e => property.SetValue(dataModel, Decimal.Parse(e.Value.ToString())))" />
}
}
</div>

我认为@nocturns2说的是正确的,你可以试试这个代码:

@if (property.PropertyType == typeof(DateTime))
{
DateTime.TryParse(YourPropertyString, out var parsedValue);
var resutl= (YourType)(object)parsedValue
<InputDate id=@"property.Name" @bind-Value="@resutl">
}

这是一个组件化版本,带有DateOnlyFields 的示例组件

@using System.Reflection
@using System.Globalization
<input type="date" value="@GetValue()" @onchange=this.OnChanged @attributes=UserAttributes />
@code {
[Parameter] [EditorRequired] public object? Model { get; set; }
[Parameter] [EditorRequired] public PropertyInfo? Property { get; set; }
[Parameter] public EventCallback ValueChanged { get; set; }
[Parameter(CaptureUnmatchedValues = true)] public IDictionary<string, object> UserAttributes { get; set; } = new Dictionary<string, object>();
private string GetValue()
{
var value = this.Property?.GetValue(Model);
return value is null
? DateTime.Now.ToString("yyyy-MM-dd")
: ((DateOnly)value).ToString("yyyy-MM-dd");
}
private void OnChanged(ChangeEventArgs e)
{
if (BindConverter.TryConvertToDateOnly(e.Value, CultureInfo.InvariantCulture, out DateOnly value))
this.Property?.SetValue(this.Model, value);
else
this.Property?.SetValue(this.Model, DateOnly.MinValue);
if (this.ValueChanged.HasDelegate)
this.ValueChanged.InvokeAsync();
}
}

使用它:

@foreach (var property in dataModel.GetType().GetProperties())
{
@if (property.PropertyType == typeof(DateOnly))
{
<div class="form-text">
<DateOnlyEditor class="form-control" Model=this.dataModel Property=property ValueChanged=this.DataChanged />
</div>
}
}
<div class="p-3">
Date: @dataModel.SelectedDate
</div>
@code {
private DataModel dataModel = new DataModel();
private EditContext? editContext;
public class DataModel
{
public DateOnly SelectedDate { get; set; } = new DateOnly(2020, 12, 25);
public int SelectedNumber { get; set; }
public decimal SelectDecimal { get; set; }
}
protected override void OnInitialized()
{
this.editContext = new EditContext(dataModel);
base.OnInitialized();
}
void DataChanged()
{
StateHasChanged();
}
}

相关内容

  • 没有找到相关文章

最新更新