I am creating a custom Blazor component, and I need the select component to update the bound field with the selected value, this part is working. I also need it to execute a method passed in from the parent component, this part is not working.
I am able to pass the method into the custom component (child), but the onclick
event of the option element of the select element is not firing.
I was able to implement this in a multiple select element, but it is not working in a select element.
How do I get this to fire and still update the bound data property?
ESelect.razor
Custom (child) select component
@inherits InputBase<string>
<div class="form-floating">
<select class="form-control form-select @CssClass" id="@Id" @bind="@CurrentValue" >
<option disabled selected></option>
@foreach(SelectOption option in Options)
{
<option [email protected] onclick="@( () => OnClick(option) )" >@option.Value</option>
}
</select>
@if (!string.IsNullOrWhiteSpace(Label))
{
<label class="form-control-label" for="@Id">@Label</label>
}
<div class="form-control-validation">
<ValidationMessage For="@ValidationFor" />
</div>
</div>
ESelect.razor.cs
(C# code for ESelect.razor)
using BebComponents.DataModels;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Forms;
using System.Linq.Expressions;
namespace BebComponents
{
public partial class ESelect
{
[Parameter, EditorRequired]
public Expression<Func<string>> ValidationFor { get; set; } = default!;
[Parameter]
public string? Id { get; set; } = "ESelect";
[Parameter]
public string? Label { get; set; }
[Parameter]
public List<SelectOption> Options { get; set; }
[Parameter]
public SelectOption? SelectedOption { get; set; }
[Parameter]
public Action? Trigger { get; set; }
protected override bool TryParseValueFromString(string? value, out string result, out string validationErrorMessage)
{
result = value;
validationErrorMessage = null;
return true;
}
public void OnClick(SelectOption option)
{
SelectedOption = option;
Trigger?.Invoke();
}
}
}
SelectOption.cs
(datamodel)
namespace BebComponents.DataModels
{
public class SelectOption
{
public int Id { get; set; }
public string Value { get; set; }
}
}
Index.razor
@page "/"
@using BebComponents
@using BebComponents.DataModels
@using static BebComponents.EDualSelect
<EditForm Model="Form" OnValidSubmit="ValidFormSubmit" class="mt-5">
<DataAnnotationsValidator />
<h3>Form Example:</h3>
<ValidationSummary />
<h3 class="mt-4">Enhanced Select</h3>
<ESelect Id="ESelect" @ref="eSelect" @bind-Value="Form.LastName" Options="@options" ValidationFor="@( () => Form.LastName )"
Label="Last Name" Trigger="EnableEnhancedSelect2"/>
<h5 class="mt-2">The last name selected is:</h5>
<p>@Form.LastName</p>
</EditForm>
Index.razor.cs
using BlazorComponents.Pages.PageModels;
using BebComponents;
using static BebComponents.EDualSelect;
using static BebComponents.ESingleSelect;
using static BebComponents.ESelect;
using BebComponents.DataModels;
namespace BlazorComponents.Pages
{
public partial class Index
{
private ESelect eSelect;
private string eSelectResult;
private readonly List<SelectOption> options = new List<SelectOption>
{
new SelectOption { Id = 1, Value = "Jones" },
new SelectOption { Id = 2, Value = "Smith" },
new SelectOption { Id = 3, Value = "Bender" },
new SelectOption { Id = 4, Value = "Baggio" },
new SelectOption { Id = 5, Value = "Allen" },
new SelectOption { Id = 6, Value = "Biggs" },
new SelectOption { Id = 7, Value = "Randall" },
new SelectOption { Id = 8, Value = "Anderson" },
new SelectOption { Id = 8, Value = "Reeves" }
};
}
}
The Simple Solution
Your existing code can be made to work with one minor addition.
You can see a working example in this Blazor Fiddle Demo Blazor Fiddle is rather limited, but I've tried to keep the code close to the original.
Your click handler never fires because an
<option>
element only fires when the parent is a<select multiple>
. Yet, in this case it's just a default single<select>
and will never fire. We might use the onchange event instead, which is usually how this is done. Yet, we could also just add a wrapper around the CurrentValue property, which would allow you to reuse the code you already have.CurrentValue is a property inherited from InputBase. And to invoke the parent supplied Trigger method you will need to wrap CurrentValue to provide that new functionality. This can be done by using the "new" keyword and calling the method in the setter as shown.
Code to add to the component:
You may also want to remove the existing onclick handler from the markup since it doesn't do anything and would only be misleading.