dotnet / aspnetcore

ASP.NET Core is a cross-platform .NET framework for building modern cloud-based web applications on Windows, Mac, or Linux.
https://asp.net
MIT License
35.19k stars 9.93k forks source link

InputSelect not selecting the binded value as expected #53988

Closed CashPJ closed 7 months ago

CashPJ commented 7 months ago

Is there an existing issue for this?

Describe the bug

In my case, I want to show the bound object whether it exists in the list or not. And it works as expected. But when I change the list and there is a bound object in the list, the html is generated as expected, but the selected object is not selected in html select control.

image

@page "/"

<p>
    Source list: @($"List{ListSource}")<br />
    Selected user Id = @SelectedUserId
</p>
<p> 
    <InputSelect @bind-Value="SelectedUserId">
        <option>Choose</option>
        @foreach(var user in DropdownList)
        {
            <option value="@user.Id" selected="@(user.Id == SelectedUserId)">@user.Name</option>
        }
        @*If not exist in list, show it in list as disabled item*@
        @if (SelectedUserId != null && DropdownList.FirstOrDefault(x => x.Id == SelectedUserId) == null)
        {
            <option value="@SelectedUserId" selected disabled>[User with Id @SelectedUserId not in list]</option>
        }
    </InputSelect>
</p>
<p>
    <input type="button" value="List 1" @onclick="()=>SwitchListSource(1)"/>
    <input type="button" value="List 2" @onclick="()=>SwitchListSource(2)" />
</p>

@code {
    public class User
    {
        public int? Id { get; set; }
        public string Name { get; set; } = string.Empty;
    }

    int ListSource = 1;
    int? SelectedUserId { get; set; }

    List<User> DropdownList
        => ListSource == 1 ? List1 : List2;

    List<User> List1 = new List<User>
    {
        new(){Id = 1, Name = "User 1"},
        new(){Id = 2, Name = "User 2"},
        new(){Id = 3, Name = "User 3"},
    };

    List<User> List2 = new List<User>
    {
        new(){Id = 1, Name = "User 1"},
        new(){Id = 4, Name = "User 4"},
        new(){Id = 3, Name = "User 3"},
    };

    void SwitchListSource(int source)
        => ListSource = source;
}

Expected Behavior

Expected behavior wold be to have selected option in html control as shown in image

image

Steps To Reproduce

Example project is available at https://github.com/CashPJ/InputSelectBlazorBug

Steps to reproduce:

  1. Choose "User 2" from dropdown
  2. Click on "List 2" button
  3. Click on "List 1" button

Exceptions (if any)

No response

.NET Version

NET 8 (8.0.101)

Anything else?

Visual studio 2022 - Version 17.8.4 dotnetinfo.txt

MariovanZeist commented 7 months ago

Hi @CashPJ The issue lies with the selected attribute on the <option> inside the <select> HTMLElement . The browser only respects the selected attribute on the initial rendering of the <select> See: So when you are rerendering the input by supplying it a new list of it won't respect the selected attribute.

If you want it to select a specific item when you change the list you can either call a javascript function to select the new item, or force Blazor to destroy the old <select> and create a new <select> so it honors the selected attribute again. You could do this for example by adding @key=ListSource to your input

CashPJ commented 7 months ago

Hi @MariovanZeist

  1. How is it that the first change of the list is displayed well?
  2. The @key mechanic solves the stated problem and allows to avoid use of javascript.

I initially solved that problem with javascript, but I consider it a quick fix. @key the mechanic for destroying the selected element you mentioned works, but it requires the developer to know about it.

In my opinion, it would be ideal to re-databind values when changing the list, which would cover the scenarios of dynamic drop downs that are common thing in the world of web applications. But that's just my opinion :)

Thanks and best regards!

SteveSandersonMS commented 7 months ago

If you use @bind or @bind-Value, that's supposed to take control over which option is selected. You can't also manually assign selected attributes since that can be inconsistent with what @bind is doing - the two will clash.

Is there a reason you can't restrict yourself to using @bind/@bind-Value to determine which option is selected?

CashPJ commented 7 months ago

@SteveSandersonMS, it was only for testing clarity. I noticed that "selected" attribute is ignored. Its existence could cause problems? I can change it into some "dummy" attribute which will be ignored by browsers.

SteveSandersonMS commented 7 months ago

I can change it into some "dummy" attribute which will be ignored by browsers.

If you're not using it to control selection then that would be wise. Otherwise it will be difficult to reason about since you have two different things both trying to control selection in possibly inconsistent ways.

dotnet-policy-service[bot] commented 7 months ago

This issue has been resolved and has not had any activity for 1 day. It will be closed for housekeeping purposes.

See our Issue Management Policies for more information.