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.36k stars 9.99k forks source link

[Blazor] Binding in select element fails #16970

Closed FelipeCostaGualberto closed 4 years ago

FelipeCostaGualberto commented 4 years ago

Hello, I'm having an issue with data binding of select element in Blazor WebAssembly. Scenario: in a CRUD application, consider each Belt must be assigned to a Management and a Situation. Model is:

    public class Belt {
        public int Id { get; set; }
        public string Text { get; set; }
        public int ManagementId { get; set; }
        public virtual Management Management { get; set; }
        public int SituationId { get; set; }
        public virtual Situation Situation { get; set; }
    }
    public class Situation {
        public int Id { get; set; }
        public string Text { get; set; }
    }
    public class Management {
        public int Id { get; set; }
        public string Text { get; set; }
    }

This is the Form component of my App:

@inject HttpClient http

<EditForm Model="@Belt" OnValidSubmit="@OnValidSubmit">
    <div class="form-group">
        <label>Belt Name</label>
        <input @bind="Belt.Text" class="form-control" />
    </div>

    <div class="form-group">
        <label>Management</label>
        <select class="form-control" @bind="Belt.ManagementId">
            <option value="">-- Select a Management --</option>
            @foreach (var item in Managements) {
                <option value="@item.Id">@item.Text</option>
            }
        </select>
    </div>

    <div class="form-group">
        <label>Situation</label>
        <select class="form-control" @bind="Belt.SituationId">
            <option value="">-- Select a Situation --</option>
            @foreach (var item in Situations) {
                <option value="@item.Id">@item.Text</option>
            }
        </select>
    </div>

    <button type="submit" class="btn btn-primary">Save</button>
</EditForm>

@code {
    [Parameter] public Belt Belt { get; set; }
    [Parameter] public EventCallback OnValidSubmit { get; set; }

    List<Management> Managements = new List<Management>();
    List<Situation> Situations = new List<Situation>();

    protected override async Task OnInitializedAsync() {
        Managements = await http.GetJsonAsync<List<Management>>("api/management");
        Situations = await http.GetJsonAsync<List<Situation>>("api/situation");
    }
}

The binding from input (Belt.Name) always works as espected.

The binding from select (Belt.ManagementId) works for the first time, then it doesn't work anymore.

The binding from select (Belt.SituationId) works for the first time and then randomly works. I really couldn't find a pattern to determine when it works or when it doesn't work.

I got no exceptions in the browser, everything runs fine.

Do you have any workaround in this situation, like, forcing its initial value in the select element? How would I do that? I tried some tricks in the OnInitializedAsync but I couldn't manage to make it work.

Further technical details

javiercn commented 4 years ago

@FelipeCostaGualberto thanks for contacting us.

@SteveSandersonMS thoughts?

FelipeCostaGualberto commented 4 years ago

Hi! Any news on this? I couldn't write CRUD applications because of this. Peharps force running a native JavaScript code in the component's initialization would work?

SteveSandersonMS commented 4 years ago

I'm afraid I wasn't able to reproduce any issue here. It's working as expected for me. The behavior I see is:

If you're seeing something different, could you please post a more complete repro, and be more specific about how it's not working?

Note that in situations where you're loading dropdown values asynchronously, it's usually a good idea not to display and bind the dropdown at all until the data has loaded. It doesn't make sense to let the user pick values even before the data has loaded - that can just confuse them. So consider doing this:

@if (Managements != null)
{
    <select class="form-control" @bind="Belt.ManagementId">
        <option value="">-- Select a Management --</option>
        @foreach (var item in Managements)
        {
            <option value="@item.Id">@item.Text</option>
        }
    </select>
}

... and set the initial value of Managements to null until the data has loaded.

mkArtakMSFT commented 4 years ago

Hi. We're closing this issue as we have heard no response from you for some time now. If you have more details and are encountering this issue please add a new reply and re-open the issue.