Search code examples
validationblazorcustom-component

Blazor validation works only when single field is present


So the thing is I have a little sandbox to play with Blazor. There is a basic form with a few fields. I'm trying to put some validation on that. The input fields are custom component, applied some bootstrap on them. In short, this is the input component:

@using System.Linq.Expressions

<div class="input-group mb-3">
    <span class="input-group-text" id=@Id>@Label</span>
    <InputText @bind-Value=@Value @oninput=OnInput type="text" class="form-control" placeholder=@PlaceHolder aria-label=@Label aria-describedby=@Id />
    <ValidationMessage For=@For />
</div>

@code {
    [Parameter]
    public string Id { get; set; }

    [Parameter]
    public string Label { get; set; }

    [Parameter]
    public string PlaceHolder { get; set; }

    [Parameter]
    public string Value 
    { 
        get => _value; 
        set
        {
            if (_value != value)
            {
                _value = value;
                ValueChanged.InvokeAsync(value);
            }
        }
    }

    private string _value;

    [Parameter]
    public EventCallback<string> ValueChanged { get; set; }

    [Parameter]
    public Expression<Func<string>> For { get; set; }

    [Parameter]
    public Func<string> OnFormChange { get; set; }

    private async Task OnInput(ChangeEventArgs e)
    {
        Value = e?.Value.ToString();
        await ValueChanged.InvokeAsync(Value);
    }
}

and a piece from the page:

...
<div class="container-sm">
    <Web.Components.Modal Id="exampleModal" Title="Fegyver bevezetése" OnSave="HandleSave" TriggerValidation="Validate" IsValid=@ModalIsValid>
        <EditForm Model=@weaponUnderEdit OnValidSubmit="HandleValidSubmit" OnInvalidSubmit="HandleInvalidSubmit">
            <DataAnnotationsValidator />
        ...
            @* <Web.Components.TextInput Id="weight" Label="Súly" PlaceHolder="Súly pl.: 0.3 (Tőr), 2.5 (Csatacsákány)" @[email protected] For="@(() => @weaponUnderEdit.Weight)" /> *@
            <Web.Components.TextInput Id="price" Label="Ár" PlaceHolder="Ár pl.: 1a 2e 3r, 1e 50r" @[email protected] For="@(() => @weaponUnderEdit.Price)" />
            <ValidationSummary />
        </EditForm>
    </Web.Components.Modal>
</div>
...

of course, there are data annotation attributes on the model:

public class CommonWeaponPrototype
{
...
    [Required, RegularExpression(@"^\d+(\.\d{1,2})?$")]
    public string Weight { get; set; }

    [Required, RegularExpression(@"^\d+(\.\d{1,2})?$")]
    public string Price { get; set; }
}

Now the validation works like that:

app

Everything there... and if the second input is uncomment... everything gone; no more validation.

So far tried to use custom validation, maybe I can do with that, using Regex check and so on... but it is really annoying that I can't make this work, and also, why custom? It is a really simple form to validate, some regex will it be.


Solution

  • I've simplified your code a little to demonstrate a working model.

    First, your custom component. Mine inherits from InputText, which uses all the built in functionality and just changes the input formatting. It implements correct binding.

    @using System.Linq.Expressions
    @inherits InputText
    
    <div class="input-group mb-3">
        <span class="input-group-text">@Label</span>
        <input @bind:get="@Value" @bind:set="OnValueChanged" @bind:event="oninput" type="text" class="form-control" placeholder=@PlaceHolder />
        <ValidationMessage For="ValueExpression" />
    </div>
    
    @code {
        [Parameter] public string? Label { get; set; }
    
        [Parameter] public string? PlaceHolder { get; set; }
    
        private Task OnValueChanged(string? value)
        {
            CurrentValueAsString = value;
            return Task.CompletedTask;
        }
    }
    

    And then the demo page:

    @page "/"
    @using System.ComponentModel.DataAnnotations
    
    <PageTitle>Home</PageTitle>
    
    <EditForm EditContext="_editContext" OnValidSubmit="this.OnValidSubmit">
        <DataAnnotationsValidator />
        <MyInput @bind-Value="_model.Weight" Label="Weight" />
        <MyInput @bind-Value="_model.Price" Label="Price" />
    </EditForm>
    
    @code {
        private Model _model = new();
        private EditContext? _editContext;
    
        protected override void OnInitialized()
        {
            _editContext = new(_model);
        }
    
        public Task OnValidSubmit()
        {
            // Do something
            return Task.CompletedTask;
        }
    
        public class Model
        {
            [Required, RegularExpression(@"^\d+(\.\d{1,2})?$")]
            public string? Weight { get; set; }
    
            [Required, RegularExpression(@"^\d+(\.\d{1,2})?$")]
            public string? Price { get; set; }
        }
    }