14
votes

I have the following Code for an InputSelect

               <InputSelect class="form-control form-control form-control-sm"
                             placeholder="Role"
                             disabled="@IsReadOnly"
                             @bind-Value="Model.Role"
                              @onchange="@RoleChanged">
                    <option value="Member">Member</option>
                    <option value="Admin">Admin</option>
                    <option value="Pioner">Pioneer</option>
                    <option value="Retailer">Retailer</option>
                </InputSelect>

And for the Code:

bool ShowCreated;
bool ShowUpdated;
bool IsReadOnly;
string SelectedRole;

public EditForm AccountForm;
public Multi.Dtos.RegistrationDto Model = new Dtos.RegistrationDto() { Role = "Member" };

public void RoleChanged(ChangeEventArgs e)
{
    SelectedRole = e.Value.ToString();
    Console.WriteLine(e.Value);
}

The RoleChange function is not invoked when i try to select a new item. Can someone point out what wrong with this. The value of the proper has changed but the Event was not called.

4
You cannot do bind-value and @onchange same time.dani herrera
I found that out during debugging. I wonder what would be the recommended approach on this.?Edu Cielo
You can implement IPropertyChange on your model and bind some action to events.dani herrera

4 Answers

21
votes

Note:

  • InputSelect is a component element, not HTML element, which is why you cannot apply to it the @onchange compiler directive. This directive is applied to elements, usually but not necessarily with the value attribute to form the so- called two way data-binding.

  • @bind-Value is a compiler directive directive instructing the compiler to produce code that enable two-way data binding between components. Applying @bind-Value to the InputSelect component requires you (already done in this case by the Blazor team) to define a parameter property named Value and an EventCallback 'delegate', conventionally named ValueChanged. Value and ValueChanged are properties of the InputSelect component.

There are a couple of ways to do this:

 <InputSelect ValueExpression="@(()=>comment.Country)" 
                                           Value="@comment.Country" 
                 ValueChanged="@((string value) => OnValueChanged(value ))">
        <option value="">Select country...</option>
        <option value="USA">USA</option>
        <option value="Britain">Britain</option>
        <option value="Germany">Germany</option>
        <option value="Israel">Israel</option>
 </InputSelect>

And

 private Task OnValueChanged(string value)
{
    // Assign the selected value to the Model 
    comment.Country = value;
   return Task.CompletedTask;
} 

You can also implement INotifyPropertyChanged.PropertyChanged Event in your Model, as dani herrera has suggested. You do that if you want to do away with the Forms components, including the EditForm, and use the normal HTML tags instead, in which case you'll be able to do something like:

<input type="text" value="@MyValue" @onchange=OnChange"/>

Of course your model class is going to be thick, and it should communicate with the EditContext....

Hope this helps...

6
votes

The request is not that old, so you may consider the following which works with EditForm (basically hook up onto another event) => at oninput eventhandler you pass The ChangeEventArgs var, and so you can get the value to process

<InputSelect @bind-Value="@labBook.StockID" @oninput="OnLabbookStockChange" class="form-control">
                            @if (lstStocks != null)
                                {
                                <option value="">select...</option>
                                @foreach (var stock in lstStocks)
                                    {
                                    <option value="@stock.StockID">@stock.TestArticle.TAName</option>
                                    }
                                }
                        </InputSelect>
0
votes

The problem is the @bind attribute makes use of the @onchange event and Blazor will not allow multiple @onchange event handlers. Workarounds in the code below:

  • Method 1: This is the vanilla example. It shows how to wire up a dropdown using an HTML select tag when you do not require an onchange event handler.
  • Method 2: This is, I think, the simplest if you need 2-way data binding AND an event handler. It uses the HTML select tag (not a Blazor component) with 1-way data binding using the "value" attribute. Since the @bind attribute is not used, we are free to attach a handler to the @onchange event. This handler, as well as handling the event, also needs to populate the property in order to achieve 2-way data binding.
  • Method 3: If you are using the whole Blazor EditForm and InputText/InputSelect/etc components infrastructure, this method may be best for you. Without the EditContext, the example shows 2-way binding using @bind-Value. In order to handle the onchange event for any component, we add an event handler (EditContext.OnFieldChanged) for the entire form. The handler checks to see which property was changed (based on the FieldName) and fires the required event handler accordingly.

I created this page to remind me of the options for this. Hope it helps anyone who finds their way here.

@page "/dropdown"

<h2 class='@(hightlight ? "bg-info" : "")'>
    User Id: @UserId
</h2>

<hr />

<h3>Using Select Tag</h3>
<p>
    <label>
        Method 1 (2-way binding but do not need to handle an onchange event):<br />
        <select @bind="UserId">
            <option value=""></option>
            @foreach (var u in users)
            {
                <option value="@u.Key">@u.Value</option>
            }
        </select>
    </label>
</p>
<p>
    <label>
        Method 2 (need to handle the onchange event; 2-way binding via the control onchange event handler):<br />
        <select value="@UserId" @onchange="OnChangeUserId">
            <option value=""></option>
            @foreach (var u in users)
            {
                <option value="@u.Key">@u.Value</option>
            }
        </select>
    </label>
</p>

<h3>Using InputSelect Tag</h3>
<EditForm EditContext="EditContext">
    <p>
        <label>
            Method 3 (2-way binding;  event handling via the EditForm EditContext.OnFieldChanged)<br />
            <InputSelect @bind-Value="@UserId">
                <option value=""></option>
                @foreach (var u in users)
                {
                    <option value="@u.Key">@u.Value</option>
                }
            </InputSelect>
        </label>
    </p>
</EditForm>

@code {
    private EditContext EditContext;
    private Dictionary<int, string> users = new Dictionary<int, string>();

    private int? UserId { get; set; }
    private bool hightlight { get; set; }

    protected override void OnInitialized()
    {
        EditContext = new EditContext(users);
        EditContext.OnFieldChanged += EditContext_OnFieldChanged;
    }

    protected override void OnParametersSet()
    {
        // simulate getting current data from the db (which would use async version of this event handler)
        users.Add(1, "Bob");
        users.Add(2, "Sue");
        users.Add(3, "R2D2");
        UserId = 2;

        base.OnParametersSet();
    }

    private void EditContext_OnFieldChanged(object sender, FieldChangedEventArgs e)
    {
        if (e.FieldIdentifier.FieldName == "UserId")
        {
            DoStuff();
        }
    }

    private void OnChangeUserId(ChangeEventArgs args)
    {
        // select tag's "value" attribute provide one-way data binding; to get 2-way,
        // we need to manually set the value of the UserId based on the ChangeEventArgs
        if (args.Value == null)
            UserId = null;
        else
            UserId = int.Parse(args.Value.ToString());

        DoStuff();
    }

    private void DoStuff()`enter code here`
    {
        hightlight = (UserId == 3);
    }
}
-1
votes

I solved this simply using the @onclick event. It fires multiple times, but the last time it fires, its the selected item.

<div>
    <EditForm Model="model">
        <InputSelect @bind-Value="model.SimulatorType" @onclick="SelectionChanged">
            @foreach (var value in Enum.GetValues(typeof(SimulatorType)))
            {
                <option>@value</option>
            }
        </InputSelect>
    </EditForm>
</div>

@code {
    class Model
    {
        public SimulatorType SimulatorType
        {
            get;set;
        }
    }

    Model model = new Model();

    private async void SelectionChanged()
    {
        await OnSelectionChanged.InvokeAsync(model.SimulatorType.ToString());
    }

    [Parameter]
    public EventCallback<string> OnSelectionChanged { get; set; }
}