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:

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;
    }());