简体   繁体   中英

Custom Validation attribute does not immediately clear error when value is fixed C# .net6 Blazor

I have a custom validation attached to a property. The validation works as expected except that when it shows that the field needs to be fixed, the error does not immediately clear when the issue is fixed like what the build in validations does. The error does not stop me from submitting my form but it seems when the error is displayed it then revalidates on the button submit click. How can I make it behave like what the default validations do ie when the field is incorrect it shows the error and when the field is corrected the error immediately goes away?

below is the implementation of the validation

public class RequireGreaterThanZeroAttribute : ValidationAttribute
{
    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        try
        {
            var objectValue = value.GetType().ToString().ToUpper() == "SYSTEM.DECIMAL" ? Convert.ToDecimal(value) : (int)value;
            if (objectValue > 0)
                return ValidationResult.Success;
            else
                return new ValidationResult($"{validationContext.DisplayName} must be greater than zero.");
        }
        catch
        {
            return new ValidationResult($"{validationContext.DisplayName} must be greater than zero.");
        }
    }

}

Below is the markup where the property is being used

<div class="col-lg-3">
   <div class="form-group">
       <label>Amount</label>
       <InputNumber @bind-Value="@Transaction.Amount"  type="text" class="form-control required"/>
   </div>
</div>

and the Model property is defined as

[RequireGreaterThanZero]
public decimal Amount { get; set; } = 0.00M;

The Submit button is like below

<div class="row">
    <div class="col-lg-6">
        <div class="form-group">
            <button type="submit" style="width:100%" class="btn btn-sm btn-primary @BuyClass @SellClass @UpdateClass @ReverseClass" data-toggle="tooltip" data-placement="top" title="Save"><i class="fa fa-save"></i> @ButtonText</button>
        </div>
    </div>
</div>

While testing the solution provided by @MrCakaShaunCurtis, what i have noted is that this issue has something to do with the DataAnnotationsValidator when you use

<DataAnnotationsValidator />

instead of

<ValidationMessage For="() => Transaction.Amount" />

This is when the message doesnt go away. With

<ValidationMessage For="() => Transaction.Amount" />

the message behaves correctly.

@MrCakaShaunCurtis, can you try using the

<DataAnnotationsValidator />

instead of the validation For and see if this also happens on your side?

Also i notice this seems to be a long oustanding issue, just stumbled on another related issue on

How to reset custom validation errors when using editform in blazor razor page

Based on the update question I think a quick explanation of how validation works will help.

DataAnnotationValidator is a component that captures the EditContext cascaded by EditForm and registers for the Validation and Field Changed events on the EditContext . When these events occur it runs whatever validations it finds on the model and logs the results to a ValidationMessageStore in the EditContext . It doesn't display anything, and should only occur once within the EditForm .

ValidationSummary and ValidationMessage are the display components. They capture the EditContext cascaded by EditForm and either display all the messages in the ValidationMessageStore , or only the relevant message for the defined model property.

Here's my minimum reproducible demo code. If you take it and run it you'll find:

  • as you change the number up and down the validation message appears and disappears.
  • as you enter a number and click/tab away from the field it updates the validation message.

So, what's the difference between your code and my demo code?

@page "/"
@using BlazorApp4.Data

<PageTitle>Index</PageTitle>

<EditForm EditContext=this.editContext OnValidSubmit=this.ValidSubmit>
    <DataAnnotationsValidator />
    <div>
        <div class="col-6">
            <div class="form-group">
                <label>Amount</label>
                <InputNumber @bind-Value="@this.model.Amount" type="text" class="form-control required" />

                <ValidationMessage For="() => this.model.Amount" />
            </div>
        </div>
    </div>
    <div class="p-2">
        <button class="btn btn-primary" type="submit">Submit</button>
    </div>

</EditForm>


@code {
    private Model model = new Model();
    private EditContext? editContext;

    protected override Task OnInitializedAsync()
    {
        this.editContext = new EditContext(this.model);
        return base.OnInitializedAsync();
    }

    private void ValidSubmit()
    {
        var x = true;
    }

    public class Model
    {
        [RequireGreaterThanZero]
        public decimal Amount { get; set; } = 0.00M;
    }
}

For reference my "Nullable" RequireGreaterThanZeroAttribute

public class RequireGreaterThanZeroAttribute : ValidationAttribute
{
    protected override ValidationResult? IsValid(object? value, ValidationContext validationContext)
    {
        var isValid = value is not null 
            && decimal.TryParse(value.ToString(), out decimal objectValue) 
            && objectValue > 0;

        return isValid
            ? ValidationResult.Success
            : new ValidationResult($"{validationContext.DisplayName} must be greater than zero.");
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM