Search code examples
asp.net-mvcfluentvalidationclient-side-validation

Fluent Validation, MVC: Triggering Client-Side RuleSet Validation on Button Click


Context

In a view, I've created one form for my view model. I've separated the form over multiple sections using the Twitter Bootstrap Wizard plugin. The user can access the next section by clicking a "Next" button. Each section has a Fluent Validation RuleSet defined for the model properties on that section. The rules I've defined in each RuleSet are compatible with Fluent Validaiton's client-side validation.

Question

Upon clicking the next button, what's the best way:

  • To get the validation state for only the current section's RuleSet on the client-side with Fluent Validation?
  • To get Fluent Validation to display client-side validation for only the current section's RuleSet?

What I've Tried

I've read the Fluent Validation start guide and this question. While they demonstrate how to achieve what I'm looking for on the server-side, they don't seem to address my client-side questions. I bold "seem" because I'm reasonably new to Fluent Validation and MVC, so, I may have misunderstood the links' content.


Solution

  • You can use commands from the jQuery validation library, which Fluent Validation uses.

    In an element in your form, define an attribute that will help you recognise a validation group. For example

    @Html.EditorFor(model => model.GroupA_TextA, new { htmlAttributes = new { @class = "form-control", data_validation_group = "GroupA" }})
    

    Use the .settings.ignore syntax from the jQuery validation library to control which groups to validate.

    I've made a class that exposes functionality to validate a group in, and the entirety of, a Fluent Validation validated form. I've included the TypeScript and transpiled JavaScript below.

    TypeScript

    /**
     * For validating a form when Fluent Validation is used for model valdiation.
     */
    interface IFluentValidationFormValidator {
        /**
         * The form to validate.
         */
        form: JQuery<HTMLElement>;
    
        /**
         * The name of the validation group to validate.
         */
        group: string;
    
        /**
         * Validate the entire form.
         */
        validate(): boolean;
    
        /**
         * Validate a validation group in the form.
         * @param group The name of the validation group to validate.
         */
        validateGroup(): boolean;
    }
    
    /**
     *
     */
    class StandardFluentValidationFormValidator implements IFluentValidationFormValidator {
        /**
         * @inheritdoc 
         */
        form: JQuery<HTMLElement>;
    
        /**
         * @inheritdoc
         */
        group: string;
    
        /**
         * @inheritdoc
         */
        validate(): boolean {
            const formValidator = this.form.validate();
            formValidator.form();
            return formValidator.valid();
        }
    
        /**
         * @inheritdoc
         */
        validateGroup(): boolean {
            // The form validator.
            const formValidator = this.form.validate();
    
            // Perform standard validation on form if the validation group is undefined.
            if (this.group === undefined) {
                formValidator.form();
                return formValidator.valid();
            }
    
            // Current group validation settings.
            const initialValidateIgnoreSetting = formValidator.settings.ignore;
    
            // Ignore all elements but the group.
            formValidator.settings.ignore += `,:not([data-validation-group=${this.group}])`;
    
            // Valdiate the form.
            formValidator.form();
    
            // Reset group validation settings.
            formValidator.settings.ignore = initialValidateIgnoreSetting;
    
            // Return the validation state.
            return formValidator.valid();
        }
    }
    

    JavaScript

    "use strict";
    var StandardFluentValidationFormValidator = (function () {
        function StandardFluentValidationFormValidator() {
        }
        StandardFluentValidationFormValidator.prototype.validate = function () {
            var formValidator = this.form.validate();
            formValidator.form();
            return formValidator.valid();
        };
        StandardFluentValidationFormValidator.prototype.validateGroup = function () {
            var formValidator = this.form.validate();
            if (this.group === undefined) {
                formValidator.form();
                return formValidator.valid();
            }
            var initialValidateIgnoreSetting = formValidator.settings.ignore;
            formValidator.settings.ignore += ",:not([data-validation-group=" + this.group + "])";
            formValidator.form();
            formValidator.settings.ignore = initialValidateIgnoreSetting;
            return formValidator.valid();
        };
        return StandardFluentValidationFormValidator;
    }());