3

I'm working on a Blazor server app where user information is collected in several steps. Parent page creates a model(let's call it MainModel), which is a collection of data from models used in child components, parent passes this model to children as a cascading parameter and children collect more info and fill in matching parameters in that model.

When the component is loaded, it loads its own model. When an input is modified, I need to check ChildModel and add it to MainModel if it passes the validation. Based on Blazor documentation, I used FieldChanged event for EditContext.


    <EditForm EditContext="editContext">
    <DataAnnotationsValidator/>
    <div class="col-md-6">
        <label for="FirstName" class="form-label">First Name</label>
        <InputText type="text" @bind-Value="Model.FirstName" class="form-control" 
     name="FirstName"/>
        <ValidationMessage For="@(() => Model.FirstName)" />
    </div>
    <div class="col-md-3">
        <label for="LastName" class="form-label">Last Name</label>
        <InputText type="text" @bind-Value="Model.LastName" class="form-control" 
        name="LastName"/>
        <ValidationMessage For="@(() => Model.LastName)" />
    </div>

    <div class="col-md-3">
        <label for="Age" class="form-label">Age</label>
        <InputText type="text" @bind-Value="Model.Age" class="form-control" name="Age"/>
        <ValidationMessage For="@(() => Model.Age)" />
    </div>
    </EditForm>


    @code{
    [CascadingParameter]
    public MainModel MainModel { get; set; }

    private ChildModel Model {get; set; }


    private EditContext editContext;

    protected override async Task OnInitializedAsync()
    {
        editContext = new EditContext(Model);
        editContext.OnFieldChanged += EditContext_OnFieldChanged;
        base.OnInitialized();
    }

    private async void EditContext_OnFieldChanged(object sender, FieldChangedEventArgs e)
    {
        if(editContext.Validate()) //<-- this shows validation messages
        {
            //copy child model to main model here
        }
    }
}

The problem is, as soon as a field is modified, this event is fired, as expected, and the model is validated which fires up the validation messages for all inputs because at that point none of the other required fields are filled in.

Is there a way to validate a model without triggering validation messages?

Maybe I need to do something with ValidationMessageStore but I haven't figured it out yet. I can toggle individual validation messages by looking at their input sibling's modified and invalid classes but I'm sure Blazor has a solution for this.

So far I don't have an answer for it so my current implementation is this:

When the EditContext.Validate() is called, I'm hiding the messages with css initially and show them only when inputs are modified and invalid:

.validation-message {
  display: none; 
}

input.modified.invalid {
  border: 1px solid red;
  position: relative; 
}

input.modified.invalid + .validation-message {
    position: absolute;
    margin-top: -10px;
    color: red;
    background: #fdf2f2;
    border: 1px solid #ffdede;
    padding: 5px;
    display: block;
    z-index: 1; 
}
PersyJack
  • 1,736
  • 1
  • 21
  • 32
  • Take a look for Fluentvalidator for .net blazor I believe there is one that works. – AliK Jun 29 '21 at 22:16
  • Related question: https://stackoverflow.com/questions/62755858/how-to-validate-a-single-field-in-blazor-editform – Eric King Jun 30 '21 at 15:12
  • 1
    @EricKing helpful but not exactly what I'm looking for. I don't want to validate an individual field, I want to know if the entire model is valid when any input changes without showing validate messages for all inputs. the reason behind this is because i have a Next button that shows up once the model is valid. – PersyJack Jun 30 '21 at 15:23
  • 1
    Cool. Figured I'd share in case it sparks some insight. – Eric King Jun 30 '21 at 15:25
  • @EricKing I appreciate it. – PersyJack Jun 30 '21 at 15:27

2 Answers2

1

What I usually do is have a bool variable that captures whether or not the editContext is valid. Something like prive bool _modelHasBeenModified {get; set;} = false;

Then I'll run the validation _modelHasBeenModified = editContext.Validate();

Then I'll reset the validations so none of the validation messages are displayed: editContext.MarkAsUnmodified(); - this way, I can still retain the model validity in _modelHasBeenModified.

sion_corn
  • 3,043
  • 8
  • 39
  • 65
1

Not a direct answer, but Chris Sainty has created a blazor wrapper for the FluentValidator @AliK referred to. Very elegant solution he describes in his book and blog. Much simpler than rolling your own.

  1. Add his Blazored Nuget package

dotnet add package FluentValidation

  1. Insert his component inside your EditForm replacing the weaker DataAnnotationsValidator:

<FluentValidationValidator @ref="fluentValidationValidator" />

  1. Create a variable to hold the validator reference:

private FluentValidationValidator fluentValidationValidator;

  1. Setup fluent validations for your child model. Really powerful tool that has been around for a while. I have been able to setup complex record level validations, duplicate checks, etc.

  2. Now you can validate in code quite easily:

    var isValid = fluentValidationValidator.Validate(opts => opts.IncludeAllRuleSets());
    if (!isValid) return;  // show errors
    // else, proceed
    
Steve Greene
  • 12,029
  • 1
  • 33
  • 54
  • I'm aware of this library but I'm not sure if i should install and implement a third party library just to know if the model is valid. Does the Validate method in your example trigger messages if it's a void method? – PersyJack Jun 30 '21 at 15:27
  • @PersyJack Depends on your use case. Knowing the model is valid is pretty key and for us this tool is indispensable for all but the most simplistic apps (i.e. those silly weather service examples). The validate method runs through your validations - which as I said can be fairly sophisticated. Not sure what you mean by "void method", but the fluent rules can be configured to only run WHEN certain conditions are met. Message display happens automatically via the .Net Core mechanisms in place for the similar DataAnnotationsValidator. – Steve Greene Jun 30 '21 at 15:46
  • Regarding the 3rd party aspect, I'd put this in the category of Automapper or Bootstrap in terms of time savings. – Steve Greene Jun 30 '21 at 15:49