InputSelect does not bind correctly to Nullable<bool>

Open Beginner friendly
#54,565 10 comments 11 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
2/5
Estimated time
1-3 hours
Newbie friendliness
78/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Active
Tech stack
csharp
Domain
frontend

Research direction

Start in src/Components/Web/src/Forms/InputSelect.cs, focusing on FormatValueAsString, and reproduce the provided EditForm example with nullable bool, int, and enum bindings. The fix is done when a null bool? displays and preserves the option with value="", while true and false continue to bind and display correctly; add regression coverage for these cases.

Written by the indexing model from the issue text.

Description

area-blazor bug feature-blazor-builtin-components feature-model-binding
Is there an existing issue for this?
  • I have searched the existing issues
Describe the bug

If you create a InputSelect control that is bound (via @bind-Value) to a bool?, it does not work in the same way as InputSelect bound to other nullable types (say int or Enum), and it never displays the null value correctly, but always shows the "false" value.

Expected Behavior

When a Nullable is bound to an InputSelect, the option with value="" should be bound to the null value. This is how other nullable types work with InputSelect, and also is the most useful.

If the model has null, then the option with value="" should be displayed when the form initially shows.

If the select is manually set to the option with value="", then that value should continue to be displayed.

Steps To Reproduce
@page "/"

<EditForm Model="Model" OnValidSubmit="Submit">
    <p>
        <label>What is your favorite color?</label>
        <InputSelect @bind-Value="Model.FavoriteColor">
            <option value="">Select a Color</option>
            <option value="Red">Red</option>
            <option value="Green">Green</option>
            <option value="Blue">Blue</option>
        </InputSelect>
    </p>

    <p>
        <label>How many legs do you have?</label>
        <InputSelect @bind-Value="Model.NumberOfLegs">
            <option value="">Select a Number</option>
            <option value="1">1</option>
            <option value="2">2</option>
        </InputSelect>
    </p>

    <p>
        <label>Are you awesome?</label>
        <InputSelect @bind-Value="Model.Awesome">
            <option value="">Select Yes or No</option>
            <option value="true">Yes</option>
            <option value="false">No</option>
        </InputSelect>
    </p>

    <button>Submit</button>
</EditForm>

@code {
    enum Color { Red, Green, Blue };
    class InputModel
    {
        public Color? FavoriteColor { get; set; } = null;
        public int? NumberOfLegs { get; set; } = null;
        public bool? Awesome { get; set; } = null;
    }

    InputModel Model = new InputModel();

    void Submit()
    { 
        Console.WriteLine($"Color: {Model.FavoriteColor} Legs: {Model.NumberOfLegs} Awesome: {Model.Awesome}");
    }
}

When this form is initially shown, the Favorite Color and Number of Legs selects show the "Select..." option, but the Awesome select shows "No". In addition, any attempt to set the "Awesome" select to "Select Yes or No" has it change immediate back to No.

Exceptions (if any)

No response

.NET Version

8.0.200

Anything else?

I think the fix is to change the src/Components/Web/src/Forms/InputSelect.cs implementation of FormatValueAsString to something like:

        protected override string? FormatValueAsString(TValue? value)
        {
            if (typeof(TValue) == typeof(bool)) {
                return (bool)(object)value! ? "true" : "false";
            }
            else if (typeof(TValue) == typeof(bool?)) {
                if (value == null)
                    return null;
                else 
                    return (bool)(object)value ? "true" : "false";
            }

            return base.FormatValueAsString(value);
        }

Luckily this method is protected, so I can fix this in the meantime by creating my own component:

    public class BetterInputSelect<TValue>: InputSelect<TValue>
    {
        protected override string? FormatValueAsString(TValue? value)
        {
            if (typeof(TValue) == typeof(bool)) {
                return (bool)(object)value! ? "true" : "false";
            }
            else if (typeof(TValue) == typeof(bool?)) {
                if (value == null)
                    return null;
                else 
                    return (bool)(object)value ? "true" : "false";
            }

            return base.FormatValueAsString(value);
        }
    }

But I think the actual InputSelect.cs should be fixed.

Dominant language
C#
Stars
38.5k
Forks
11.6k
Avg merge
2d 3h
Merged PRs (30d)
243

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from dotnet/aspnetcore

All issues in dotnet/aspnetcore

Similar issues

More C# issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.