Search code examples
c#blazor.net-8.0

Extend InputRadio adding defult css style


I am trying to render a custom radio button in Blazor only adding a default css class to the component. So I thought I could just extend the InputRadio component and modify the css classes that are defined.

So I have a css class called "foo" and Im trying to make a custom component FooRadio by default have all the stuff with InputRadio but also class="foo". I dont want to have to actually add it manually.

I tried doing this..

@inherits InputRadio<TValue>
@{
   base.BuildRenderTree(__builder);
}

Now I thought there may have been a Css property I could tap into and overwrite, but I dont seem to have access to one. The InputRadio does merge the AdditionalAttributes with the Context.FieldCss, but the only thing available for me is the AdditionalAttributes and it is a ReadOnly Dictionary. Also InputRadio requires a TValue, and I am not sure how to inherit it and also give it a TValue. I assume my custom Radio Button will need to have a as well.

So, I started just building a simple <input type="radio".../> but I dont know all the callbacks or bindings I will need to add in the component to make it function like the InputRadio.

I know InputRadio requires you to be inside an InputRadioGroup which is fine. Ideally Id like to be able to do something like this..

<InputRadioButtonGroup>
    <InputRadio...>
    <FooRadio...>
    <InputRadio...>
</InputRadioButtonGroup>

Again, FooRadio and InputRadio will act the same. Its just FooRadio will have a css class of "foo" without actually declaring it on the FooRadio tag

Hope this is enough info. If not, I can give more info. Maybe I am approaching this in a much more complicated way than needs to be.

Thank you for any assistance.


Solution

  • InputRadioGroup and InputRadio are implemented in a slightly different way to most InputBase<TValue> controls. This makes them harder to customize without building new versions based on the original codebase.

    InputRadioGroup is a component with no UI output: the class attribute has no effect. It provides a wrapper context for the InputRadio child instances that manages state and cascades an InputRadioContext.

    The Css for each InputRadio is based on the value provided in the class attribute and some colour formatting based on validation information in the EditContext.

    So to achieve what you want, we need to create a CustomInputRadio component that wraps an instance of InputRadio rather than inheriting from it.

    Here's an example that applies the standard Bootstrap formatting.

    @typeparam TValue
    
    <div class="form-check">
        <InputRadio class="@this.CssClass" TValue="TValue" Name="ContinentGroup" Value="Value" />
        <label class="@this.LabelCssClass">
            @_label
        </label>
    </div>
    
    @code {
        [Parameter] public TValue? Value { get; set; }
        [Parameter] public string? Label { get; set; }
        [Parameter] public string CssClass { get; set; } = "form-check-input";
        [Parameter] public string LabelCssClass { get; set; } = "form-check-label";
    
        private string? _label => this.Label ?? Value?.ToString(); 
    }
    

    Which you then use like this:

    @page "/"
    
    <PageTitle>Home</PageTitle>
    
    <h1>Hello, world!</h1>
    
    <EditForm EditContext="_editContext">
        <InputRadioGroup Name="ContinentGroup" @bind-Value="_model.Value">
            @foreach (var continent in _continents)
            {
                <CustomInputRadio Value="continent" />
            }
        </InputRadioGroup>
    </EditForm>
    
    <div class="bg-dark text-white m-2 p-2">
        <pre>Value: @_model.Value</pre>
    </div>
    
    @code {
        private EditContext? _editContext;
        private Model _model = new();
    
        protected override void OnInitialized()
        {
            _editContext = new(_model);
        }
    
        private List<String> _continents = new() { "Europe", "Oceania", "Africa" };
    
        public class Model
        {
            public string? Value { get; set; }
        }
    }
    

    You can find the source code here - https://github.com/dotnet/aspnetcore/blob/main/src/Components/Web/src/Forms/InputRadio.cs

    If you want to build and combine strings into Css strings see how to create a CssBuilder utility - https://github.com/EdCharbeneau/BlazorComponentUtilities

    enter image description here