使用 Blazor 创建一个接受列表的 InputSelect 组件
Create an InputSelect component that accepts lists with Blazor
使用 Blazor InputSelect
您可以迭代组件中的列表项 ChildContent
但是
我想创建一个自定义 Blazor(WebAssembly 版本 5)InputSelect
,它可以接受要在 select 中呈现的任何对象的列表,代码可能如下所示:
<CustomInputSelect @bind-Value="@myEntity.CountryId" For="@(()=> myEntity.CountryId)" List="countries"
ValueField="@(a => a.Id)" DisplayField="@(a => a.CountryName)" ></CustomInputSelect>
甚至这个:
<CustomInputSelect @bind-Value="@myEntity.Country" For="@(()=> myEntity.Country)" List="countries"
ValueField="Id" DisplayField="CountryName" ></CustomInputSelect>
(注意: 我使用了 For
属性 来帮助编译器推断 CountryId
的类型并进行验证。这是组件的 @typeparam
。)
我尝试了很多方法,但 none 行得通,所以我在下面解释它们:
我尝试对列表使用 dynamic
类型,但 Blazor 似乎不支持动态类型,因为 razor 生成的代码中存在错误。
我尝试为组件使用多个 @typeparam
,一个用于值,一个用于列表项。而且好像也不支持
@inherits InputSelect
//或添加另一个类型参数@typeparam TListItem
和
<select>
@foreach (var item in List)
{
< option value="???">???</option >
}
</select>
@code{
[Parameter] public Expression<Func<TValue>>? For { get; set; }
[Parameter] public List<dynamic> List { get; set; }// or List<TListItem>
[Parameter] public Expression<Func<TListItem>>? ValueField { get; set; }
[Parameter] public Expression<Func<string>>? DisplayField { get; set; }
}
我的目标是将任何类型的列表发送到 InputSelect
,而 @typeparam
用于绑定。
我以前构建并使用过这个
@typeparam TItem
<div class="form-control-wrapper">
<select class="form-control-label" @onchange="ChangeHandler">
@if (ShowDefaultOption)
{
<option value="0" hidden disabled>- Please Select -</option>
}
@foreach (var (id, item) in idDictionary)
{
<option value="@id">@Selector(item).ToString()</option>
}
</select>
</div>
@code {
[Parameter] public IList<TItem> Items { get; set; }
[Parameter] public Func<TItem, object> Selector { get; set; }
[Parameter] public EventCallback<TItem> ValueChanged { get; set; }
[Parameter] public bool ShowDefaultOption { get; set; } = true;
private Dictionary<Guid, TItem> idDictionary;
protected override void OnInitialized()
{
idDictionary = new Dictionary<Guid, TItem>();
Items.ToList().ForEach(x => idDictionary.Add(Guid.NewGuid(), x));
}
private async Task ChangeHandler(ChangeEventArgs args)
{
if (idDictionary.TryGetValue(Guid.Parse(args.Value.ToString()), out var selectedItem))
{
await ValueChanged.InvokeAsync(selectedItem);
}
}
}
然后使用可以这样使用:
<MySelect ValueChanged="handleStringChange" TItem="string" Items="stringItems" Selector="(x => x)" />b
<p>@_selectedStringItem</p>
@code {
IList<string> stringItems = new List<string>()
{
"Random",
"String",
"Content",
"ForDemo"
};
string _selectedStringItem;
void handleStringChange(string value) => _selectedStringItem = value;
}
或者用一个对象:
<MySelect ValueChanged="handleChange" TItem="Person" Items="items" Selector="(x => x.Firstname)" />
@if (_selectedItem is object)
{
<p>Selected person: Name: @_selectedItem.Firstname, Age: @_selectedItem.Age</p>
}
@code {
class Person
{
public string Firstname { get; set; }
public int Age { get; set; }
}
IList<Person> items = new List<Person>
{
new Person {Firstname = "John Doe", Age = 26},
new Person {Firstname = "Jane Doe", Age = 23}
};
Person _selectedItem;
void handleChange(Person value) => _selectedItem = value;
}
使用 Blazor InputSelect
您可以迭代组件中的列表项 ChildContent
但是
我想创建一个自定义 Blazor(WebAssembly 版本 5)InputSelect
,它可以接受要在 select 中呈现的任何对象的列表,代码可能如下所示:
<CustomInputSelect @bind-Value="@myEntity.CountryId" For="@(()=> myEntity.CountryId)" List="countries"
ValueField="@(a => a.Id)" DisplayField="@(a => a.CountryName)" ></CustomInputSelect>
甚至这个:
<CustomInputSelect @bind-Value="@myEntity.Country" For="@(()=> myEntity.Country)" List="countries"
ValueField="Id" DisplayField="CountryName" ></CustomInputSelect>
(注意: 我使用了 For
属性 来帮助编译器推断 CountryId
的类型并进行验证。这是组件的 @typeparam
。)
我尝试了很多方法,但 none 行得通,所以我在下面解释它们:
我尝试对列表使用
dynamic
类型,但 Blazor 似乎不支持动态类型,因为 razor 生成的代码中存在错误。我尝试为组件使用多个
@typeparam
,一个用于值,一个用于列表项。而且好像也不支持@inherits InputSelect //或添加另一个类型参数@typeparam TListItem
和
<select>
@foreach (var item in List)
{
< option value="???">???</option >
}
</select>
@code{
[Parameter] public Expression<Func<TValue>>? For { get; set; }
[Parameter] public List<dynamic> List { get; set; }// or List<TListItem>
[Parameter] public Expression<Func<TListItem>>? ValueField { get; set; }
[Parameter] public Expression<Func<string>>? DisplayField { get; set; }
}
我的目标是将任何类型的列表发送到 InputSelect
,而 @typeparam
用于绑定。
我以前构建并使用过这个
@typeparam TItem
<div class="form-control-wrapper">
<select class="form-control-label" @onchange="ChangeHandler">
@if (ShowDefaultOption)
{
<option value="0" hidden disabled>- Please Select -</option>
}
@foreach (var (id, item) in idDictionary)
{
<option value="@id">@Selector(item).ToString()</option>
}
</select>
</div>
@code {
[Parameter] public IList<TItem> Items { get; set; }
[Parameter] public Func<TItem, object> Selector { get; set; }
[Parameter] public EventCallback<TItem> ValueChanged { get; set; }
[Parameter] public bool ShowDefaultOption { get; set; } = true;
private Dictionary<Guid, TItem> idDictionary;
protected override void OnInitialized()
{
idDictionary = new Dictionary<Guid, TItem>();
Items.ToList().ForEach(x => idDictionary.Add(Guid.NewGuid(), x));
}
private async Task ChangeHandler(ChangeEventArgs args)
{
if (idDictionary.TryGetValue(Guid.Parse(args.Value.ToString()), out var selectedItem))
{
await ValueChanged.InvokeAsync(selectedItem);
}
}
}
然后使用可以这样使用:
<MySelect ValueChanged="handleStringChange" TItem="string" Items="stringItems" Selector="(x => x)" />b
<p>@_selectedStringItem</p>
@code {
IList<string> stringItems = new List<string>()
{
"Random",
"String",
"Content",
"ForDemo"
};
string _selectedStringItem;
void handleStringChange(string value) => _selectedStringItem = value;
}
或者用一个对象:
<MySelect ValueChanged="handleChange" TItem="Person" Items="items" Selector="(x => x.Firstname)" />
@if (_selectedItem is object)
{
<p>Selected person: Name: @_selectedItem.Firstname, Age: @_selectedItem.Age</p>
}
@code {
class Person
{
public string Firstname { get; set; }
public int Age { get; set; }
}
IList<Person> items = new List<Person>
{
new Person {Firstname = "John Doe", Age = 26},
new Person {Firstname = "Jane Doe", Age = 23}
};
Person _selectedItem;
void handleChange(Person value) => _selectedItem = value;
}