Search code examples
c#blazor

Bootstrap radio button group


I'm creating a form in Blazor and I like to have a segment like the following example

enter image description here

Using Bootstrap, I have this code for that

<div class="btn-group" role="group" aria-label="Basic radio toggle button group">
    <input type="radio" id="btnradio1-@Id" class="btn-check" name="btnradio1-@Id" 
        autocomplete="off" checked @bind-value="Value">
    <label class="form-label btn btn-outline-primary" for="btnradio1-@Id">
        Yes</label>

    <input type="radio" id="btnradio2-@Id" class="btn-check" name="btnradio2-@Id" 
        autocomplete="off" @bind-value="Value">
    <label class="form-label btn btn-outline-primary" for="btnradio2-@Id">
        No</label>

    <input type="radio" id="btnradio3-@Id" class="btn-check" name="btnradio3-@Id" 
        autocomplete="off" @bind-value="Value">
    <label class="form-label btn btn-outline-primary" for="btnradio3-@Id">
        Sometimes</label>
</div>

and in the code, I have this parameter:

[Parameter] public string Id { get; set; } = Guid.NewGuid().ToString();
[Parameter] public string? Value { get; set; }

When I click on an input, I want to change the Value with the correspond value of the input I clicked. For example, if I click on No, I want that the Value is No.


Solution

  • Using html radio buttons to do this is a little problematic. I've found it easier to build a custom InputBase component using the Bootstrap ButtonGroup with normal buttons.

    Here's a simple version:

    @using System.Diagnostics.CodeAnalysis
    
    @inherits InputBase<string>
    
    <div class="btn-group mt-1 me-2 mb-3" data-toggle="buttons">
        @foreach( var item in this.Options)
        {
            <button type="button" class="@this.GetButtonCSS(item.Value)" @onclick="() => ValueHasChanged(item.Value)">
                @item.Value
            </button>
        }
    </div> 
    
    @code {
        [Parameter, EditorRequired] public IEnumerable<SelectOption<string>> Options { get; set; } = Enumerable.Empty<SelectOption<string>>();
        [Parameter] public string SelectedCss { get; set; } = "btn btn-primary";
        [Parameter] public string UnSelectedCss { get; set; } = "btn btn-outline-primary";
    
        protected override bool TryParseValueFromString(string? value, out string result, [NotNullWhen(false)] out string? validationErrorMessage)
        {
            result = value ?? string.Empty;
            validationErrorMessage = null;
            return true;
        }
    
        private string GetButtonCSS(string value)
        {
            return value.Equals(this.Value)
                ? this.SelectedCss
                : this.UnSelectedCss;
        }
    
        private void ValueHasChanged(string value)
        {
            this.CurrentValueAsString = value;
        }
    }
    

    The option object:

    public readonly record struct SelectOption<TValue>(TValue Value, string Label);
    

    And a demo page:

    @page "/"
    
    <PageTitle>Home</PageTitle>
    
    <h1>Hello, world!</h1>
    
    Welcome to your new app.
    
    <EditForm Model="_model">
        <RadioButtonInput Options="_options" @bind-Value="_model.Value1" />
        <RadioButtonInput Options="_options" @bind-Value="_model.Value2" />
        <RadioButtonInput Options="_options" @bind-Value="_model.Value3" />
    </EditForm>
    
    <div class="bg-dark text-white m-2 p-2">
        <pre>Value1: @_model.Value1</pre>
        <pre>Value2: @_model.Value2</pre>
        <pre>Value3: @_model.Value3</pre>
    </div>
    
    @code {
        private List<SelectOption<string>> _options = new() {
            new("Yes", "Yes"),
            new("No", "No"),
            new("Sometimes", "Sometimes")
        };
    
        private Model _model = new();
    
        public class Model
        {
            public string Value1 { get; set; } = "N";
            public string Value2 { get; set; } = "N";
            public string Value3 { get; set; } = "N";
        }
    }
    

    enter image description here