Reputation: 3261
With Blazor InputSelect
you have iterate over list items in the component ChildContent
but
I want to create a custom Blazor (WebAssembly version 5) InputSelect
that could accept a list of any object to render in the select, the code could be like the followings :
<CustomInputSelect @bind-Value="@myEntity.CountryId" For="@(()=> myEntity.CountryId)" List="countries"
ValueField="@(a => a.Id)" DisplayField="@(a => a.CountryName)" ></CustomInputSelect>
or even this :
<CustomInputSelect @bind-Value="@myEntity.Country" For="@(()=> myEntity.Country)" List="countries"
ValueField="Id" DisplayField="CountryName" ></CustomInputSelect>
(Note: I used the For
property to help compiler to infer the type of CountryId
and also for validation. This is @typeparam
of the component.)
I tried so many approaches but none of the worked so I explain them below:
I tried using dynamic
type for the list but it seems that Blazor does not supports dynamic types because there was error in razor generated code.
I tried to use multiple @typeparam
for the component, one for the value and one for the list items. And it seems that it does not support it either.
@inherits InputSelect //or add another typeparam @typeparam TListItem
And
<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; }
}
My goal is to send a list of any type to InputSelect
while @typeparam
is used for binding.
Upvotes: 4
Views: 12299
Reputation: 300
I've previous built and used this
@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);
}
}
}
Then use can use it like this:
<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;
}
Or with an object:
<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;
}
Upvotes: 9