Search code examples
asp.net-coreblazorblazor-webassembly

DataAnnotationsValidator doesn't work with a custom component


I created this custom component

@inherits InputLabelPers1Base
<div class="text-input">
    <input @attributes="AdditionalAttributes" value="@Value" type="text" placeholder=@InputPlaceHolder @onchange="OnInputValueChanged"  >
    <label for="input1">@LabelValue </label>
</div>

that inherits from this class :

 public class InputLabelPers1Base : InputBase<String>
    {
        
        [Parameter]
        public string InputPlaceHolder { get; set; }

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

        protected Task OnInputValueChanged(ChangeEventArgs e)
        {
            Value = (String)e.Value;

            return ValueChanged.InvokeAsync(Value);
        }

        protected override bool TryParseValueFromString(string value, [MaybeNull] out string result, [NotNullWhen(false)] out string validationErrorMessage)
        {
            result = value;
            validationErrorMessage = null;
            return true;
        }

      
    }

And I added the component to an EditForm: the validation work fine for the built in componant InputText but not for my custom component

<div class="ClientCardContainer">
    <EditForm Model="@Client" OnInvalidSubmit="@Save">
        <DataAnnotationsValidator />
        <ValidationSummary />
        <div class="ClientInfoGridContainer">
        <InputLabelPerso1 @bind-Value="Client.CL_NOM" LabelValue="Nom/Société" InputPlaceHolder="Nom/Société"></InputLabelPerso1>
        <InputLabelPerso1 @bind-Value="Client.CL_CODE" LabelValue="Code" InputPlaceHolder="Code Client"></InputLabelPerso1>
        <InputLabelPerso1 @bind-Value="Client.CL_VILLE" LabelValue="Ville" InputPlaceHolder="Ville client"></InputLabelPerso1>
      
        <InputText @bind-Value="Client.CL_NOM"></InputText> // **Works Fine**
        <button type="submit">Submit</button>
        </div>
    </EditForm>
</div>

Solution

  • Copy and test... Comments and explanations in the code...

    InputLabelPers1Base.cs

        public class InputLabelPers1Base : InputBase<String>
            {
                [Parameter]
                public string InputPlaceHolder { get; set; }
        
                [Parameter]
                public string LabelValue { get; set; }
        
                protected async Task OnInputValueChanged(ChangeEventArgs e)
                {
                    // Use the CurrentValueAsString instead of Value 
                    // Value = (String)e.Value;
                    // You shouldn't call the base class ValueChanged.
                    // When you assign the new value to the 
                    // CurrentValueAsString property, the code in the 
                    // CurrentValue property called by CurrentValueAsString
                    // takes care of updating the field bound to your
                    // InputLabelPerso1 component
    
                    CurrentValueAsString = e.Value.ToString();
                    await Task.CompletedTask;
                }
        
                protected override bool TryParseValueFromString(string value, [MaybeNull] out string result, [NotNullWhen(false)] out string validationErrorMessage)
                {
                    result = value;
                    validationErrorMessage = null;
                    return true;
                }
        
        
            }
    

    InputLabelPerso1.razor

        @inherits InputLabelPers1Base
        <div class="text-input">
            <input @attributes="AdditionalAttributes" value="@CurrentValue" type="text" placeholder=@InputPlaceHolder @onchange="OnInputValueChanged">
            <label for="input1">@LabelValue </label>
        </div>
    

    Index.razor

        @page "/"
        @using System.ComponentModel.DataAnnotations
        
        <EditForm Model="@Client" OnValidSubmit="HandleValidSubmit">
            <DataAnnotationsValidator />
            <ValidationSummary />
            <div class="ClientInfoGridContainer">
                <InputLabelPerso1 @bind-Value="Client.Value1" LabelValue="Nom/Société" InputPlaceHolder="Nom/Société"></InputLabelPerso1>
                <ValidationMessage For="@(() => Client.Value1)" />
        
                <InputText @bind-Value="Client.Value2"></InputText>
                <ValidationMessage For="@(() => Client.Value2)" />
                <button type="submit">Submit</button>
            </div>
        </EditForm>
    
        @code
            {
               
            private Customer Client = new Customer();
            private void HandleValidSubmit()
            {
                // Called only when the model is valid
                Console.WriteLine("Handled");
            }
            private void HandleInvalidSubmit()
            {
                // Called whenever the model is invalid
                Console.WriteLine("invalid");
            }
        
            public class Customer
            {
                [Required]
                public string Value1 { get; set; }
                [Required]
                public string Value2 { get; set; }
        
            }
          
        }