jeudi 27 août 2020

Realtime Model Datavalidation C# Server side Blazor. Exclude default/placeholder values from validation

Using server side Blazor I have several pages where i make use of the provided data validation tools in ASP to do realtime data validation. One example is as such:

<EditForm EditContext="PageEditContext">
  <DataAnnotationsValidator />
  <div class="form-row">
    <div class="form-group col-md-6">
      <label for="fooID">Foo ID</label>
        <InputNumber class="form-control" id="fooID" placeholder="fooID" @bind-Value=SelectedFoo.FooId />
        <ValidationMessage For="@(() => SelectedFoo.FooId)" />
    </div>
    <div class="form-group col-md-6">
      <label for="inputFooName">Foo Name</label>
        <InputText class="form-control" id="inputFooName" placeholder="Foo name" @bind-Value=SelectedFoo.FooName />
        <ValidationMessage For="@(() => SelectedFoo.FooName)" />
    </div>
  </div>
</EditForm>

The data-bound model, in this case, has the following properties along with validation attributes

[Required]        
[Range(1, int.MaxValue, ErrorMessage = "Only positive number allowed")]
public int FooId { get; set; }

[Required]        
[StringLength(20, MinimumLength = 5, ErrorMessage = "FooName should be between 5 and 20 characters")]
public string FooName { get; set; }

My reason for using EditContext instead of a direct Model definition is that I can implement realtime validation by calling the validate method when needed/on event raise.

To create a shared functionality I made a base class as such:

protected EditContext PageEditContext;

protected virtual void AttachEditContext<T>(T model)
{
  PageEditContext = new EditContext(model);
  PageEditContext.OnFieldChanged += EditContext_OnFieldChanged;
}

protected abstract void EditContext_OnFieldChanged(object sender, FieldChangedEventArgs e);

Then in my child I implement the EditContext_OnFieldChanged method to call the validation.

protected override void EditContext_OnFieldChanged(object sender, FieldChangedEventArgs e)
{            
  PageEditContext.Validate();
}

Which has the desired effect of doing the validation if a field is edited. And remains persistent, if I leave a field in an improper state and edit the next. It will keep displaying the error message.

However on the creation of a new object, in this case, a new Foo. My validation will fire after editing the first field.

private void CreateNewFoo()
{
  SelectedFoo = new Foo(0, null); //Foo(int FooID, string FooName)
  AttachEditContext(SelectedRole);
}

Which... I do not want, I do want validation on all fields if they have a value. But if they're on their default value and/or placeholder value in HTML. The validation should ideally be skipped (not succeeded, since my styling changes valid fields).

Now, this leads me to my actual question, how to achieve this? I feel like there should be a straight forward way, but the more research I've done, the less I'm convinced of this.

What I've thought of but run into problems with are:

  • Implementing a custom validation attribute. But I don't know how to short-circuit it (I do not want a valid/invalid state. It should simply be skipped if my value is 'default').
  • Using per field validation leads to problems with persistence where if FooName is considered invalid, and I start editing FooID, I lost my validation of FooName. (And I'm not sure how to work around this.)
  • Access the EditContext validations and deleting any 'false' invalids by checking if they have default values. But To my knowledge, this can't easily be done since the EditContext doesn't allow me to do so.

Other common 'solutions' that I know of (quoted because I don't see them fit the desired case).

  • Separate models for new objects. Where you loosen the validation and/or disable.
  • On submit validation only. Doesn't have realtime validation/feedback.

I'm now currently thinking in circles, and am wondering whether I should take a step back and take an entirely different approach. Unfortunately, I don't really have anybody to spar with about this level of problems. And I keep having the feeling I'm missing something more straight forward, or am simply going the wrong approach.

Any help or insights would be appreciated.

Aucun commentaire:

Enregistrer un commentaire