Blazor: How to use the onchange event in <select> when using @bind also?

SelectBindOnchangeBlazor

Select Problem Overview


I need to be able to run a function after a selection in made in a <select>. The issue is I'm also binding with @bind and I get a error when I try to use @onchange stating that it is already in use by the @bind. I tried using @onselectionchange, but that does nothing(doesn't run the function). I could forget the @bind and just assign @onchange to a function, but I'm not sure how to pass the selected value to the function.

I have the following code:

<select @bind="@SelectedCustID" @ @onchange="@CustChanged" class="form-control">
    @foreach (KeyGuidPair i in CustList)
    {
        <option value="@i.Value">@i.Text</option>
    }
</select>

Thanks.

Select Solutions


Solution 1 - Select

<select @bind="MyProperty">
<option>Your Option<option>
</select>

@code  {
    private string myVar;

    public string MyProperty
    {
        get { return myVar; }
        set
        {
            myVar = value;
            SomeMethod();
        }
    }

    private void SomeMethod()
    {
        //Do something
    }
}

Solution 2 - Select

@bind is essentially equivalent to the having both value and @onchange, e.g.:

<input @bind="CurrentValue" />

Is equivalent to:

<input value="@CurrentValue" @onchange="@((ChangeEventArgs e) => CurrentValue = e.Value.ToString())" />

Since you've already defined @onchange, instead of also adding @bind, just add value to prevent the clash:

<select value="@SelectedCustID" @onchange="@CustChanged" class="form-control">
    @foreach (KeyGuidPair i in CustList)
    {
        <option value="@i.Value">@i.Text</option>
    }
</select>

Source: https://docs.microsoft.com/en-us/aspnet/core/blazor/components/data-binding?view=aspnetcore-3.1

Solution 3 - Select

This seems to be a popular confusion. Firstly you cant use @onchange since it would internally be used by @bind. You should be able to access the selected value from the setter of your CustChanged property. Based on what you are trying to do with your CustChanged, you may not even need to manually check when this value is updated. For instance, if your intent is to use CustChanged in your UI directly or indirectly (within Linq or something), the UI would automatically update with CustChanged value when your <select> is changed. Hence, for most use cases I don't see the need to check when it was updated.

To use @onchange, you can bind it to a function something like this:

public void OnUpdated(ChangeEventArgs e)
{
    var selected = e.Value;
}

Solution 4 - Select

Just add @bind-value and @bind-vale:event with @onchnage exactly as below. Note the lower case letters. This works for me without any issue.

<select @bind-value="variablenametokeepselectedvalue" @onchange="yourmethodname" @bind-value:event="oninput">
 <option value="1">Test</option>
</select>

Solution 5 - Select

You can avoid @bind altogether (if you're using a foreach):

<select @onchange=@(handleChange)>
	@foreach (var option in _options){
		<option value=@option.Id selected=@(SelectedId == option.Id)>@option.Name</option>
	}
</select>

@code {
	public record Person(int Id, string Name);
	public int SelectedId { get; set; }
	public List<Person> _options = new List<Person>() {
		new Person(1,"A"),
		new Person(2,"B"),
		new Person(3,"C")
	};

	public void handleChange(ChangeEventArgs args) {
		Console.WriteLine(args.Value);
		SelectedId = Int32.Parse(args.Value.ToString());
	}

}

Some sordid details: I was getting some weird behavior when trying to use F# with server-side Blazor. In short, setting the List of options to the result of an Entity Framework query (mapped to a list of records) wouldn't @bind properly, but using a dummy list of options that were C# classes and not F# records did work. It wasn't due to it being records though, because if I set the list to the EF query and then immediately set it to a dummy list of records, it still didn't @bind properly - but it did work if I commented out the EF line.

Solution 6 - Select

Please check this example. It is using @bind but upon setting the value it triggers @onchange event

<div class="form-group">
    <label for="client">Client Name</label>
    <select id="client" @bind="CheckSelected" class="form-control">
       <option value="selected1">selected1</option>
       <option value="selected2">selected2</option>
    </select>
</div>
@code {

    private string selectedItem {get; set;}

    private string CheckSelected
    {
        get
        {
            return selectedItem;
        }
        set
        {
            ChangeEventArgs selectedEventArgs = new ChangeEventArgs();
            selectedEventArgs.Value = value;
            OnChangeSelected(selectedEventArgs);
        }
    }

    private void OnChangeSelected(ChangeEventArgs e)
    {
        if (e.Value.ToString() != string.Empty)
        {
            selectedItem = e.Value.ToString();
        }
    }

}

Solution 7 - Select

My recommendation is, if possible, use an EditForm wrapper around your form elements. Then you can detect a change of any of the form elements, in one place. This is good for, for example, a bunch of search filters. Any change in any of the filters should trigger another query of the data, etc.

Example of how to trigger event on form changes is here:

https://stackoverflow.com/questions/61576373/blazor-editform-change-events

Solution 8 - Select

<div class="form-group">
    <label for="client">Client Name</label>
    <select class="form-control" @onchange="@((e) => { myVar = e.Value.ToString(); MyMethod(); })">
       <option value="val1">val1</option>
       <option value="val2">val2</option>
    </select>
</div>

This is how I was able to set the property while calling for a method in the @onchange event.

-Blazor Server -Dotnet Core 3.1

Solution 9 - Select

According to Microsoft's documentation, this is their preferred way of handling this problem:

<InputText Value="@NewPaymentAmount" class="mdl-textfield__input"
           ValueExpression="() => NewPaymentAmount"
           ValueChanged="(string value) => ValidateAmount(value)" />



private void ValidateAmount(string amount)
{
    NewPaymentAmount = amount;
    
    // Do validation or whatever
}

Or the async way:

<InputText Value="@NewPaymentAmount" class="mdl-textfield__input"
           ValueExpression="() => NewPaymentAmount"
           ValueChanged="async (string value) => await ValidateAmountAsync(value)" />



private async Task ValidateAmountAsync(string amount)
{
    NewPaymentAmount = amount;
    
    // Do validation or whatever
}

Solution 10 - Select

I use oninput to essentially have bind and onchange.

oninput triggers when the input value is changed whereas bind/onchange triggers when the element loses focus and the input has changed.

This might not fit every scenario however as it wll trigger on every input while typing, depending on your need or for inputs such as selects, radios, etc. it should be suitable.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionMikeView Question on Stackoverflow
Solution 1 - SelectZsolt BendesView Answer on Stackoverflow
Solution 2 - SelectSaeb AminiView Answer on Stackoverflow
Solution 3 - SelectNeville NazeraneView Answer on Stackoverflow
Solution 4 - SelectBenWView Answer on Stackoverflow
Solution 5 - SelectDharmaTurtleView Answer on Stackoverflow
Solution 6 - SelectlevinjayView Answer on Stackoverflow
Solution 7 - SelectRandy GamageView Answer on Stackoverflow
Solution 8 - SelectJose R CamiloView Answer on Stackoverflow
Solution 9 - SelectSerj SaganView Answer on Stackoverflow
Solution 10 - SelectVereonixView Answer on Stackoverflow