【发布时间】:2020-09-30 23:56:38
【问题描述】:
我想为 Blazor 编写一个自定义下拉列表组件,部分原因是现有的 InputSelect 组件不绑定到字符串和枚举类型以外的任何东西。这对我来说还不够好,因为我的模型具有我希望绑定到下拉列表的 int 和可为空的 int 类型属性。到目前为止,我有这个:
@using System.Globalization
@typeparam TValue
@typeparam TData
@inherits InputBase<TValue>
<select id="@Id" @bind="CurrentValueAsString" class="f-select js-form-field">
@if (!string.IsNullOrWhiteSpace(OptionLabel) || Value == null)
{
<option value="">@(OptionLabel ?? "-- SELECT --")</option>
}
@foreach (var item in Data)
{
<option value="@GetPropertyValue(item, ValueFieldName)">@GetPropertyValue(item, TextFieldName)</option>
}
</select>
<span>Component Value is: @Value</span>
@code {
[Parameter]
public string Id { get; set; }
[Parameter]
public IEnumerable<TData> Data { get; set; } = new List<TData>();
[Parameter]
public string ValueFieldName { get; set; }
[Parameter]
public string TextFieldName { get; set; }
[Parameter]
public string OptionLabel { get; set; }
private Type ValueType => IsValueTypeNullable() ? Nullable.GetUnderlyingType(typeof(TValue)) : typeof(TValue);
protected override void OnInitialized()
{
base.OnInitialized();
ValidateInitialization();
}
private void ValidateInitialization()
{
if (string.IsNullOrWhiteSpace(ValueFieldName))
{
throw new ArgumentNullException(nameof(ValueFieldName), $"Parameter {nameof(ValueFieldName)} is required.");
}
if (string.IsNullOrWhiteSpace(TextFieldName))
{
throw new ArgumentNullException(nameof(TextFieldName), $"Parameter {nameof(TextFieldName)} is required.");
}
if (!HasProperty(ValueFieldName))
{
throw new Exception($"Data type {typeof(TData)} does not have a property called {ValueFieldName}.");
}
if (!HasProperty(TextFieldName))
{
throw new Exception($"Data type {typeof(TData)} does not have a property called {TextFieldName}.");
}
}
protected override bool TryParseValueFromString(string value, out TValue result, out string validationErrorMessage)
{
validationErrorMessage = null;
if (ValueType == typeof(string))
{
result = (TValue)(object)value;
return true;
}
if (ValueType == typeof(int))
{
if (string.IsNullOrWhiteSpace(value))
{
result = default;
}
else
{
if (int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var parsedValue))
{
result = (TValue)(object)parsedValue;
}
else
{
result = default;
validationErrorMessage = $"Specified value cannot be converted to type {typeof(TValue)}";
return false;
}
}
return true;
}
if (ValueType == typeof(Guid))
{
validationErrorMessage = null;
if (string.IsNullOrWhiteSpace(value))
{
result = default;
}
else
{
if (Guid.TryParse(value, out var parsedValue))
{
result = (TValue)(object)parsedValue;
}
else
{
result = default;
validationErrorMessage = $"Specified value cannot be converted to type {typeof(TValue)}";
return false;
}
}
return true;
}
throw new InvalidOperationException($"{GetType()} does not support the type '{typeof(TValue)}'. Supported types are string, int and Guid.");
}
private string GetPropertyValue(TData source, string propertyName)
{
return source.GetType().GetProperty(propertyName)?.GetValue(source, null).ToString();
}
private bool HasProperty(string propertyName)
{
return typeof(TData).GetProperty(propertyName) != null;
}
private bool IsValueTypeNullable()
{
return Nullable.GetUnderlyingType(typeof(TValue)) != null;
}
}
在父组件中我可以这样使用它:
<DropDownList Id="@nameof(Model.SelectedYear)"
@bind-Value="Model.SelectedYear"
Data="Model.Years"
ValueFieldName="@nameof(Year.Id)"
TextFieldName="@nameof(Year.YearName)">
</DropDownList>
这很好用,模型绑定到下拉列表,当下拉列表值改变时,父模型上的值也会改变。但是我现在想在我的父母身上捕获这个值变化事件并做一些自定义逻辑,主要是根据所选年份加载一些额外的数据。我的猜测是我需要一个自定义 EventCallback 但我尝试的一切都会导致某种构建或运行时错误。看来,如果我的组件继承自 InputBase,那么我能做的事情就非常有限。
谁能告诉我如何从父组件中捕获子组件的值变化?
【问题讨论】:
-
为什么不从 InputText 派生?
-
@enet 我需要呈现 HTML 选择标签而不是输入类型文本。我实际上尝试从 InputSelect 继承失败,因为它只处理字符串和枚举绑定 - 当您尝试绑定到任何其他类型的属性时失败。
-
您的代码有效吗?请参阅我的答案示例...如果需要,这就是您应该使用其他类型的方法。
-
@enet 是的,我的代码正在运行。我没有尝试过你的,但它似乎很合理。我想我可以这样做,但我不喜欢必须通过某种循环自己生成选项标签的想法。我也希望该组件为我做到这一点。
标签: blazor asp.net-blazor