question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Publish New Official Blazor Package: FluentValidation.Blazor

See original GitHub issue

System Details

  • FluentValidation version: 8.5.0
  • Web Framework version (eg ASP.NET Core 2.1, MVC 5, WebApi 2. Delete if not applicable): ASP.NET Core 3.0 Blazor

Issue Description

This is a plug-and-play, ready-to-use, tested component for automatically enabling FluentValidation with ASP.NET Core 3.0 Blazor:

using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Forms;
using System;
using System.Collections.Generic;

namespace FluentValidation
{
    /// <summary>
    /// Add Fluent Validator support to an EditContext
    /// </summary>
    public class FluentValidator : ComponentBase
    {
        [CascadingParameter]
        EditContext CurrentEditContext { get; set; }

        [Inject]
        IServiceProvider ServiceProvider { get; set; }

        IValidator Validator { set; get; }

        protected override void OnInitialized()
        {
            if (CurrentEditContext == null)
            {
                throw new InvalidOperationException($"{nameof(DataAnnotationsValidator)} requires a cascading " +
                    $"parameter of type {nameof(EditContext)}. For example, you can use {nameof(DataAnnotationsValidator)} " +
                    $"inside an EditForm.");
            }

            this.GetValidator();
            this.AddValidation();
        }

        private void GetValidator()
        {
            var validatorType = typeof(IValidator<>);
            var formType = CurrentEditContext.Model.GetType();
            var formValidatorType = validatorType.MakeGenericType(formType);
            this.Validator = ServiceProvider.GetService(formValidatorType) as IValidator;

            if (this.Validator == null)
            {
                throw new InvalidOperationException($"FluentValidation.IValidator<{formType.FullName}> is"
                    + " not registered in the application service provider.");
            }
        }

        private void AddValidation()
        {
            var messages = new ValidationMessageStore(CurrentEditContext);

            // Perform object-level validation on request
            CurrentEditContext.OnValidationRequested +=
                (sender, eventArgs) => ValidateModel((EditContext)sender, messages);

            // Perform per-field validation on each field edit
            CurrentEditContext.OnFieldChanged +=
                (sender, eventArgs) => ValidateField(CurrentEditContext, messages, eventArgs.FieldIdentifier);
        }

        private void ValidateModel(EditContext editContext, ValidationMessageStore messages)
        {
            // ATTENTION: DO NOT USE Async Void + ValidateAsync
            // Explanation: Blazor UI will get VERY BUGGY for some reason if you do that. (Field CSS lagged behind validation)
            var validationResults = Validator.Validate(editContext.Model);

            messages.Clear();

            foreach (var error in validationResults.Errors)
            {
                var fieldID = editContext.Field(error.PropertyName);
                messages.Add(fieldID, error.ErrorMessage);
            }

            editContext.NotifyValidationStateChanged();
        }

        private void ValidateField(EditContext editContext, ValidationMessageStore messages, /*in*/ FieldIdentifier fieldIdentifier)
        {
            var vselector = new FluentValidation.Internal.MemberNameValidatorSelector(new[] { fieldIdentifier.FieldName });
            var vctx = new ValidationContext(editContext.Model, new FluentValidation.Internal.PropertyChain(), vselector);

            messages.Clear(fieldIdentifier);

            var validationResults = Validator.Validate(vctx);

            foreach (var error in validationResults.Errors)
            {
                var fieldID = editContext.Field(error.PropertyName);
                messages.Add(fieldID, error.ErrorMessage);
            }

            editContext.NotifyValidationStateChanged();
        }
    }
}

Usage Tutorial

It’s very simple. It replaces built-in ASP.NET Core <DataAnnotationValidator> Blazor Component without any additional settings in the codebase.

<EditForm Model="Form">
    <FluentValidation.FluentValidator></FluentValidation.FluentValidator>
    <div class="form-group">
        <label for="name">Name</label>
        <InputText id="name" class="form-control" @bind-Value="Form.Name"></InputText>
        <ValidationMessage For="() => Form.Name"></ValidationMessage>
    </div>
    <div class="form-group">
        <label for="email">Email</label>
        <InputText id="email" type="email" class="form-control" @bind-Value="Form.Email"></InputText>
        <ValidationMessage For="() => Form.Email"></ValidationMessage>
    </div>
    <div class="form-group">
        <label for="phone_number">Phone Number</label>
        <InputText id="phone_number" type="tel" class="form-control" @bind-Value="Form.PhoneNumber"></InputText>
        <ValidationMessage For="() => Form.PhoneNumber"></ValidationMessage>
    </div>
    <div class="form-group">
        <label for="password">Password</label>
        <InputText id="password" type="password" class="form-control" @bind-Value="Form.Password"></InputText>
        <ValidationMessage For="() => Form.Password"></ValidationMessage>
    </div>
    <div class="form-group">
        <label for="password_verify">Verify Password</label>
        <InputText id="password_verify" type="password" class="form-control" @bind-Value="Form.VerifyPassword"></InputText>
        <ValidationMessage For="() => Form.VerifyPassword"></ValidationMessage>
    </div>
    <div class="form-group">
        <button type="submit" class="btn btn-primary">
            <i class="fas fa-chevron-up"></i>
            Submit
        </button>
    </div>
</EditForm>

The component will automatically grab registered IValidator<FormModelType> from the DI, therefore library consumers will only need to register their validator implementation in the DI:

services.AddTransient<IValidator<CreateAccountFormModel>, CreateAccountFormModelValidator>();

Demo Screenshot

image

Automatic client-side validation. Excellent integration with custom validation.

Reference Implementation

https://docs.microsoft.com/en-us/aspnet/core/blazor/forms-validation?view=aspnetcore-3.0#validation-support

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Comments:6 (2 by maintainers)

github_iconTop GitHub Comments

1reaction
JeremySkinnercommented, Sep 27, 2019

Thanks for the suggestion. I am not planning on providing an official Blazor integration package for FluentValidation for a couple of reasons:

  • I do not personally have any experience with it
  • the maintenance overhead for integration packages like this is huge.

By including something like this in the project it’s making a commitment for me personally to support this code, write and maintain integration tests, write all the documentation, re-test everything whenever a new preview release comes out and commit to answering questions posted here about it. This is a massive cost to me. I already do all this for the asp.net integration and it’s exhausting - I’m afraid I don’t have the time (or inclination) to do this for blazor too.

This is the perfect opportunity for the community to step in and commit to maintain an integration package to ease the burden from me. I’d suggest you speak to @mrpmorris who is doing a great job on the blazor-fluentvalidation package at https://github.com/mrpmorris/blazor-validation. If you want to help, I would recommend you head over there and see if there’s anything you can get involved in.

Thanks for understanding.

0reactions
mrpmorriscommented, Sep 28, 2019
  1. Sorry, I don’t understand what you mean

  2. The CSS class on Inputs not updating is a known bug in Blazor.

  3. Blazor-Validation allows you to have multiple validation approaches for the same app. So you could have DataAnnotationsValidator and FluentValidationValidator validating the form at the same time simply by adding a simple <Validate/> component. It is changed throughout the app with a single line of code, which discovers validators for you.

  4. I don’t know what you mean by this either.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Blazor — FluentValidation documentation
Built with Sphinx using a theme provided by Read the Docs. Read the Docs v: latest. Versions: latest.
Read more >
Using FluentValidation for Forms Validation in Blazor
In this post, I'm going to show you how you can use the popular FluentValidation library, instead of data annotations, to validate the...
Read more >
mrpmorris/blazor-validation
Blazor -Validation. Blazor-Validation is a validation agnostic library for validating forms in Blazor- Microsoft aspdotnet Blazor project.
Read more >
Blazor Fluent Validation
On this episode of Blazor StateHasChanged() we explored validation using the FluentValidation package. Validation is still a concept in ...
Read more >
Integrating FluentValidation with Blazor - Steve Sanderson
FluentValidation is a popular validation library for .NET Core by Jeremy Skinner. It has some advantages over .
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found