Search code examples
javascriptjqueryasp.net-mvcvalidationhtml-helper

Ignoring required's on an entire form using jQuery.Validate()


I'm trying to ignore the required validation on an entire form using jQuery validate but can't seem to get it working, this is on an MVC project using an @html helper so I can't add names to the elements to validate, I also cant remove the validation then add it all back using jQuery since not all the fields are required, I'll put my current java below and any help will be much appreciated

$("body").on("click", ".next", function(e) {
  e.preventDefault();
  var btn = $(this);
  var form = btn.closest("form");
  form.validate({
    rules: {
      required: false
    }
  });
  //check if form is valid
  if (form.valid()) {
    var out = form.validate({
      rules: {
        required: true
      }
    });
    if (form.valid()) {
      alert("valid and complete");
    } else {
      alert("valid but not complete");
    }
  } else {
    showSysMessage("Invalid Data. Please Check the Data in the Highlighted Fields", {
      color: "#FFB347"
    });
  };
});

Edit

This validator needs to validate multiple different partials with over 300 fields so using the names of each individual element would be beyond impractical, so I'm wondering if there is a way to validate a form and not validate the required on the inputs, hope this helps

Edit 2

Im my model I have the fields that are required with a [Required] over them, But I would like stage one on the validation to check if the data entered is valid, If it is it will save these to the database and allow page change, then the second step of the validation needs to check if all the elements that have been set to required in the model have been filled in, and if they have been return that the form has been Completed

Here is a sample of my model

    [Required]
    [Display(Name = "Title")]
    public int? TitleID { get; set; }

    [Required]
    [Display(Name = "First Name")]
    public string FirstName { get; set; }

    [Display(Name = "Middle Names")]
    public string Middlename { get; set; }

    [Required]
    [Display(Name = "Surname")]
    public string Surname { get; set; }

    [Required]
    [Display(Name = "Date of Birth")]
    public DateTime? DateOfBirth { get; set; }

    [Required]
    [Display(Name = "Nationality")]
    public string Nationality { get; set; }

    [Required]
    [Display(Name = "Martial Status")]
    public int? StatusMaritalID { get; set; }

    [Required]
    [Display(Name = "NI Number")]
    public string NINumber { get; set; }

    [Required]
    [Display(Name = "Have you been known by diffrent names?")]
    public bool? IsKnownByOtherNames { get; set; }

    [Required]
    [Display(Name = "Phone Number")]
    public string PhoneNumber { get; set; }

    [Required]
    [Display(Name = "Mobile Number")]
    public string MobileNumber { get; set; }

    [Required]
    [Display(Name = "Email")]
    public string Email { get; set; }

Here is a sample of the controller that gets any filled out data

    model.TitleID = data.TitleID;

    model.FirstName = data.FirstName;

    model.Middlename = data.Middlename;

    model.Surname = data.Surname;

    model.DateOfBirth = data.DateOfBirth;

    model.Nationality = data.Nationality;

    model.StatusMaritalID = data.StatusMaritalID;

    model.NINumber = data.NINumber;

    model.IsKnownByOtherNames = data.IsKnownByOtherNames;

    model.PhoneNumber = data.PhoneNumber;

    model.MobileNumber = data.MobileNumber;

    model.Email = data.Email;

Here is an example of the save controller

                data.TitleID = model.TitleID;

                data.FirstName = model.FirstName;

                data.Middlename = model.Middlename;

                data.Surname = model.Surname;

                data.DateOfBirth = model.DateOfBirth;

                data.Nationality = model.Nationality;

                data.StatusMaritalID = model.StatusMaritalID;

                data.NINumber = model.NINumber;

                data.IsKnownByOtherNames = model.IsKnownByOtherNames;

                data.PhoneNumber = model.PhoneNumber;

                data.MobileNumber = model.MobileNumber;

                data.Email = model.Email;

Here is the view being used

<h2>Personal Details</h2>

<div class="row-fluid">
    <div class="span3">
        @Html.LabelFor(x => x.TitleID)
        @Html.DropDownListFor(x => x.TitleID, Model.Titles, "-- Please Select --", new { @class = "input-block-level" })
        @Html.LabelFor(x => x.DateOfBirth)
        @Html.TextBoxFor(x => x.DateOfBirth, "{0:dd/MM/yyyy}", new { @class = "input-block-level date-picker" })
    </div>
    <div class="span3">
        @Html.LabelFor(x => x.FirstName)
        @Html.TextBoxFor(x => x.FirstName, new { @class = "input-block-level" })
        @Html.LabelFor(x => x.Nationality)
        @Html.TextBoxFor(x => x.Nationality, new { @class = "input-block-level" })
    </div>
    <div class="span3">
        @Html.LabelFor(x => x.Middlename)
        @Html.TextBoxFor(x => x.Middlename, new { @class = "input-block-level" })
        @Html.LabelFor(x => x.StatusMaritalID)
        @Html.DropDownListFor(x => x.StatusMaritalID, Model.StatusMaritals, "-- Please Select --", new { @class = "input-block-level" })
    </div>
    <div class="span3">
        @Html.LabelFor(x => x.Surname)
        @Html.TextBoxFor(x => x.Surname, new { @class = "input-block-level" })
        @Html.LabelFor(x => x.NINumber)
        @Html.TextBoxFor(x => x.NINumber, new { @class = "input-block-level" })
    </div>
</div>

<h3>Contact Details</h3>

<div class="row-fluid">
    <div class="span3">
        @Html.LabelFor(x => x.PhoneNumber)
        @Html.TextBoxFor(x => x.PhoneNumber, new { @class = "input-block-level" })
    </div>
    <div class="span3">
        @Html.LabelFor(x => x.MobileNumber)
        @Html.TextBoxFor(x => x.MobileNumber, new { @class = "input-block-level" })
    </div>
    <div class="span6">
        @Html.LabelFor(x => x.Email)
        @Html.TextBoxFor(x => x.Email, new { @class = "input-block-level", type="email" })
    </div>
</div>
<div class="row-fluid">
    <div class="checkbox span12" style="padding-left:0;">
        @Html.CheckBox("IsKnownByOtherNames", Model.IsKnownByOtherNames != null && (bool)Model.IsKnownByOtherNames)
        @Html.LabelFor(x => x.IsKnownByOtherNames)
    </div>
</div>

And finally here is my entire save script

$("body").on("click", ".next", function(e){
            debugger;
            e.preventDefault();
            var btn = $(this);
            var form = btn.closest("form");
            form.validate({
                rules: {
                    required: false
                }
            });
            //check if form is valid
            if (form.valid()) {
                var out = form.validate({
                    rules: {
                        required: true
                    }
                });
                if (form.valid()) { 
                    // disable the button to prevent multiple posts
                    btn.prop("disabled", true);
                    // serialise the form fields into an array
                    var fields = form.serializeArray();
                    // create a blank object to stuff the parameters into for the ajax call
                    var params = {};
                    // loop over the array and create a property for each item in array
                    // in the format expected by the controller action
                    $.each(fields, function (index, element) {
                        params[element.name] = element.value;
                    });
                    //loop over all checkboxes and see if they are checked or not
                    var checkboxes = form.find("input:checkbox")
                    $.each(checkboxes, function (index, element) {
                        params[element.name] = $(element).is(":checked");
                    });
                    // Make the ajax post call
                    // passing the parameters
                    $.post("UpdateCheck", params)
                    .success(function (data) {
                        alert("valid and complete");
                        // if fail alert the user of fail with error
                        if (data == false) {
                            //Create a toast message alerting of fail with reason
                            showSysMessage("Failed to Save. (Data Error)", { color: "#FFB347" });
                            // re-enables button
                            btn.prop("disabled", false);
                        } else {
                            btn.prop("disabled", false);
                            var currentpage = parseInt($(".TinyDancer").attr("data-currentpage"))
                            var newpage = currentpage + 1;
                            $.post("Page"+PageNos[newpage], {id:@Model.ID }).success(function(html){
                                $("#WizardStage").val(PageNos[newpage]);
                                $(".page[data-pageno='" + PageNos[newpage] + "']").parent().addClass("active").siblings().removeClass("active");
                                $(".TinyDancer").slideUp(function(){$(".TinyDancer").html(html).promise().done(function() {$(".TinyDancer").slideDown();});});
                                $(".TinyDancer").attr("data-currentpage", newpage);
                                $("form").removeData("validator");
                                $("form").removeData("unobtrusiveValidation");
                                $.validator.unobtrusive.parse("form");
                            });
                        }
                    }).fail(function () {
                        showSysMessage("Failed to Save. (Network Transport Error))", { color: "#FFB347" });
                    });
                } else {
                    alert("valid but not complete");}
            }else{
                showSysMessage("Invalid Data. Please Check the Data in the Highlighted Fields", { color: "#FFB347" });
            };
        });

Solution

  • Thanks to Stephen Muecke for pointing me in the right direction for this one, I have finally come up with a fully dynamic solution which should work for every single field without requiring separate pages/partials or models.

    To do this Firstly at the point you load your page/partial run this script to add a class called isRequired to all of the required elements

    var fields = $("form").find("input[data-val]");
    $.each(fields, function (index, element) {
        $(element).addClass("isRequired")
    });
    

    Thee next thing do do is to format the validation script like so

    $("body").on("click", ".next", function(e){
        e.preventDefault();
        var btn = $(this);
        var form = btn.closest("form");
    
        $.each($('.isRequired'), function (index, element) {
            $(element).rules('add', {
                required: false   // set a new rule
            });
        });
    
        form.validate();
        //check if form is valid
        if (form.valid()) {
            $.each($('.isRequired'), function (index, element) {
                $(element).rules('add', {
                    required: true   // set a new rule
                });
            });
            form.validate();
            if (form.valid()) { 
                alert("Form is Completed and Valid")
            } else {
                alert("Form is Valid but not complete");}
        }else{
            alert("Data Enter in some of the fields is of an Invalid Type")
        };
    });