51

I have a dynamically generated form with input fields with the same name (for example: "map"). I do not have the option of changing the field names or generating unique field names because the form handler code (Perl/CGI) is designed to handle an array of input values (in this case @map).

How can I use the JQuery Validate Plugin to validate a form in such a situation? Specifically I would want exactly one element of the submitted array to have a certain fixed value. I am currently using a custom event handler that creates a JSON object with serializeArray() and then traverses it to ensure that the condition is met. But since I have used the Validate Plugin in the rest of the application, I was wondering if such a case may be handled using the same plugin here too.

Thank you for your attention.

Gurunandan Bhat
  • 3,544
  • 3
  • 31
  • 43
  • 2
    It would be so much easier to provide you with an answer if you posted some code with your question. – ichiban May 31 '09 at 14:37
  • Sorry, I should have. Here is some representative code. In my application, the form is generated dynamically from a template. The HTML form has the following fields: .... I want to check that at least one of the fields contains the value (say) "email" I discovered after posting here that this is not possible using the Validate Plugin. See the plugin authors mail here: http://tr.im/n0lQ As the author suggests you can do this for checkboxes and radio buttons – Gurunandan Bhat Jun 01 '09 at 04:39
  • @Ya.Perelman you should consider answer from "scampbell", it is working and very simple. – EGL 2-101 Jun 09 '12 at 07:59

12 Answers12

72

Instead of changing the source file jquery.validation you can simply override the function you need to edit only in the pages that requires it.

An example would be:

$.validator.prototype.checkForm = function() {
    //overriden in a specific page
    this.prepareForm();
    for (var i = 0, elements = (this.currentElements = this.elements()); elements[i]; i++) {
        if (this.findByName(elements[i].name).length !== undefined && this.findByName(elements[i].name).length > 1) {
            for (var cnt = 0; cnt < this.findByName(elements[i].name).length; cnt++) {
                this.check(this.findByName(elements[i].name)[cnt]);
            }
        } else {
            this.check(elements[i]);
        }
    }
    return this.valid();
};

This might not be the best solution, but at least it avoids editing source files, that could be replaced later, when a new version releases. Where your overridden function might or might not break.

peterh
  • 11,875
  • 18
  • 85
  • 108
Ahmed Galal
  • 1,218
  • 1
  • 12
  • 24
  • yeah you need 50 rep to comment on other's answers afaik. I voted you up :) – totallyNotLizards Sep 27 '12 at 13:35
  • this is a good approach. But it returns error if one of the duplicated names is in hidden. then a checkbox will determine the visibility of the that emails. So if the duplcated names are shown, this works fine. if it doesnt, there is an error – lilsizzo Jun 07 '18 at 07:40
59

Old thread I know but I came across it in search of the fix to the same problem.

A more elegant solution has been posted here: http://web-funda.blogspot.com/2009/05/jquery-validation-for-array-of-input.html

You simply edit jquery.validate.js and change the checkForm to

    checkForm: function() {
    this.prepareForm();
    for ( var i = 0, elements = (this.currentElements = this.elements()); elements[i]; i++ ) {
        if (this.findByName( elements[i].name ).length != undefined && this.findByName( elements[i].name ).length > 1) {
            for (var cnt = 0; cnt < this.findByName( elements[i].name ).length; cnt++) {
                    this.check( this.findByName( elements[i].name )[cnt] );
            }
        } else {
            this.check( elements[i] );
        }
    }
    return this.valid();
}
scampbell
  • 970
  • 7
  • 11
  • 3
    @scampbell Thanks, this would be the best/nicest solution! Though, modifying the source code can be tricky when you want to update to newer versions. Did you try to propose this update in the original implementation? Cheers! – Stef Mar 05 '12 at 06:33
  • @Stef No I didnt at the time. I see it has a repository on Github now. Did you try that to see if it has been updated? https://github.com/jzaefferer/jquery-validation – scampbell Mar 05 '12 at 09:06
  • 2
    This works fine but thers's a problem with this solution that it also adds validation to disabled elements which were working as default. Please, try to solve this problem also. – Vaibhav Gupta Jun 02 '12 at 19:23
  • 1
    @NaoiseGolden yes it is as simple as that. just copy paste. – EGL 2-101 Jun 09 '12 at 07:56
  • @zsitro did you try the Github link above? I dont use this anymore but it would be nice if somebody had fixed it properly. The Github repo was last updated 12 days ago as I type. Please report back if it works properly now. Thanks. – scampbell Jul 27 '13 at 09:16
  • 3
    @scampbell the point being that editing a core file is not recommended. But yes it is simple. Until you upgrade. – Naoise Golden Jul 30 '13 at 16:22
42

I spent some time searching and trying different things when finally I tried the most trivial way of doing validation on multiple fields. Each field and it's clones share a class unique to each set. I just looped through the inputs with that class and added my validation rules as usual. I hope this might help someone else.

    $("#submit").click(function(){
    $("input.years").each(function(){
        $(this).rules("add", {
            required: true,
            messages: {
                required: "Specify the years you worked"
            }
        } );            
    });

    $("input.employerName").each(function(){
        $(this).rules("add", {
            required: true,
            messages: {
                required: "Specify the employer name"
            }
        } );            
    }); 

    $("input.employerPhone").each(function(){
        $(this).rules("add", {
            required: true,
            minlength: 10,
            messages: {
                required: "Specify the employer phone number",
                minlength: "Not long enough"
            }
        } );            
    }); 

    $("input.position").each(function(){
        $(this).rules("add", {
            required: true,
            messages: {
                required: "Specify your position"
            }
        } );            
    });             

    $("input.referenceName").each(function(){
        $(this).rules("add", {
            required: true,
            messages: {
                required: "Specify the reference name"
            }
        } );            
    });         

    $("input.referencePhone").each(function(){
        $(this).rules("add", {
            required: true,
            minlength: 10,
            messages: {
                required: "Specify your reference phone number",
                minlength: "Not long enough"
            }
        } );            
    });

// Now do your normal validation here, but don't assign rules/messages for the fields we just set them for





});
  • So can it be as simple as "$("#registration").validate();" that we add after the dynamic rules/messages you added via .each()? – bafromca Oct 20 '11 at 18:51
  • @BrettAlton You'll need to use `$("#registration").validate().element(this);` for that. See [this comment](http://stackoverflow.com/a/8897924/707372) for more details on how this can be done by still defining all validation rules in one place and only applying them on submit. – Stef Mar 05 '12 at 05:42
  • 1
    Note that this does not work if you need different validation rules for different fields with the same name. – jsalvata May 05 '13 at 11:10
11

I just learned from a mail by the Plugins author, Jörn Zaefferer, that validation requires field names to be unique except for radio buttons and check boxes.

Chris Frederick
  • 5,482
  • 3
  • 36
  • 44
Gurunandan Bhat
  • 3,544
  • 3
  • 31
  • 43
5

Jason's answer will do the trick, but I didn't want to add extra click events on every form I did this on.

In my case, I have the validation plugin consider names ending with '[]' different even though they may have identical fieldnames. To do this, I overwrote these two internal methods after jquery.validate.js loads.

$.validator.prototype.elements= function() {
var validator = this,
    rulesCache = {};

// select all valid inputs inside the form (no submit or reset buttons)
// workaround $Query([]).add until http://dev.jquery.com/ticket/2114 is solved
return $([]).add(this.currentForm.elements)
.filter(":input")
.not(":submit, :reset, :image, [disabled]")
.not( this.settings.ignore )
.filter(function() {
    !this.name && validator.settings.debug && window.console && console.error( "%o has no name assigned", this);

    // select only the first element for each name (EXCEPT elements that end in []), and only those with rules specified
    if ( (!this.name.match(/\[\]/gi) && this.name in rulesCache) || !validator.objectLength($(this).rules()) )
        return false;

    rulesCache[this.name] = true;
    return true;
});
};


$.validator.prototype.idOrName = function(element) {

// Special edit to get fields that end with [], since there are several [] we want to disambiguate them
// Make an id on the fly if the element doesnt have one
if(element.name.match(/\[\]/gi)) {
    if(element.id){
        return element.id;
    } else {
        var unique_id = new Date().getTime();

        element.id = new Date().getTime();

        return element.id;
    }
}

return this.groups[element.name] || (this.checkable(element) ? element.name : element.id || element.name);
};
  • This solution when coupled with the rules() method of adding rules [see Jason's answer above i.e. $(this).rules("add", ... )], works great except for some internal methods such as numberOfInvalids() which only return a count of 1. – wes Jan 07 '12 at 04:09
3

Simply use an unused attribute of the input to store the original name, then just rename with it's index attached:

function addMultiInputNamingRules(form, field, rules){    
    $(form).find(field).each(function(index){
    $(this).attr('alt', $(this).attr('name'));
    $(this).attr('name', $(this).attr('name')+'-'+index);
    $(this).rules('add', rules);
});

}

function removeMultiInputNamingRules(form, field){    
    $(form).find(field).each(function(index){
    $(this).attr('name', $(this).attr('alt'));
    $(this).removeAttr('alt');
});

}

Then after you set your validator:

addMultiInputNamingRules('#form-id', 'input[name="multifield[]"]', { required:true });

and when you've finished validating, revert back like so:

removeMultiInputNamingRules('#form-id', 'input[alt="multifield[]"]');

-- Hope this helps!

  • Works like a charm.! make sure you don't have id attribute along with name, it won't work otherwise. wasted my 1 hour just to find this. – JD-V Apr 07 '17 at 20:51
3

I'm using "jQuery validation plug-in 1.7".

The problem why multiple "$(:input)" elements sharing the same name are not validated

is the $.validator.element method:

elements: function() {
        var validator = this,
            rulesCache = {};

        // select all valid inputs inside the form (no submit or reset buttons)
        // workaround $Query([]).add until http://dev.jquery.com/ticket/2114 is solved
        return $([]).add(this.currentForm.elements)
        .filter(":input")
        .not(":submit, :reset, :image, [disabled]")
        .not( this.settings.ignore )
        .filter(function() {
            !this.name && validator.settings.debug && window.console && console.error( "%o has no name assigned", this);

            // select only the first element for each name, and only those with rules specified
            if ( this.name in rulesCache || !validator.objectLength($(this).rules()) )
                return false;

            rulesCache[this.name] = true;
            return true;
        });
    },

The condition

if ( this.name in rulesCache ||.....

evaluates for the second and next elements sharing the same name true....

The solution would be having the condition:

(this.id || this.name) in rulesCache

Excuse me, JS puritans, that (this.id || this.name) is not at 100%...

Of course, the

rulesCache[this.name] = true;

line must be changed appropriately as well.

So the $.validator.prototype.elements method would be:

$(function () {
if ($.validator) {
    //fix: when several input elements shares the same name, but has different id-ies....
    $.validator.prototype.elements = function () {

        var validator = this,
            rulesCache = {};

        // select all valid inputs inside the form (no submit or reset buttons)
        // workaround $Query([]).add until http://dev.jquery.com/ticket/2114 is solved
        return $([]).add(this.currentForm.elements)
        .filter(":input")
        .not(":submit, :reset, :image, [disabled]")
        .not(this.settings.ignore)
        .filter(function () {
            var elementIdentification = this.id || this.name;

            !elementIdentification && validator.settings.debug && window.console && console.error("%o has no id nor name assigned", this);

            // select only the first element for each name, and only those with rules specified
            if (elementIdentification in rulesCache || !validator.objectLength($(this).rules()))
                return false;

            rulesCache[elementIdentification] = true;
            return true;
        });
    };
}

});

Motlicek Petr
  • 767
  • 9
  • 10
3

Here is how I did it. A bit easier than previously proposed methods:

function validateTab(tab) {
    var valid = true;
    $(tab).find('input').each(function (index, elem) {
        var isElemValid = $("#registrationForm").validate().element(elem);
        if (isElemValid != null) { //this covers elements that have no validation rule
            valid = valid & isElemValid;
        }
    });

    return valid;
}

In my case I have a wizard (of 3 steps) which turned out to be even more complex as I don't want to validate all fields at once. I basically place components in tabs and if first tab is valid, I move to the next, until I get to last one, after which I submit all data. Thus the tab parameter there is the actual tab element (which is a div). I then loop through all input elements children to my tab and check them for validity.

Everything else is standard.


Just for completeness here is the rest of the code: how the form submit is done and how my validator looks like:

<a href="javascript:moveToNextTab(1)" class="button next">Submit</a>

And here the js function called:

function moveToNextTab(currentTab) {
    var tabs = document.getElementsByClassName("tab");
    //loop through tabs and validate the current one.
    //If valid, hide current tab and make next one visible.
}

I'm using these validation rules (which I create on JQuery.ready):

$("#registrationForm").validate({
    rules: {
        birthdate: {
            required: true,
            date: true
        },
        name: "required",
        surname: "required",
        address: "required",
        postalCode: "required",
        city: "required",
        country: "required",
        email: {
            required: true,
            email: true
        }
    }
});
Stef
  • 2,603
  • 2
  • 18
  • 28
1

For me this was solved very easily by disabling the debug

 $("#_form").validate({
    debug:false,
    //debug: true,
    ...
    });
Ebrahim
  • 1,740
  • 2
  • 25
  • 31
1

Maybe I'm missing the point, but since the validator doesn't work with multiple names (tried... failed!) I changed my form to dynamically change the names, set the rules, then unset the names on submit.

Two methods (ignore the wlog stuff, it just outputs to the console):

// convert the field names into generated ones to allow fields with the same names 
// to be validated individually. The original names are stored as data against the
// elements, ready to be replaced. The name is replaced with
// "multivalidate-<name>-<id>", e.g. original => 'multivalidate-original-1'

function setGeneratedNamesWithValidationRules(form, fields, rules) {

    var length = fields.length;

    for (var i=0; i < length; ++i ){
        var name = fields[i];

        var idCounter = 0;  
        // we match either the already converted generator names or the original
        $("form [name^='multivalidate-" + name + "'], form [name='" + name + "']").each(function() {
            // identify the real name, either from the stored value, or the actual name attribute
            var realName = $(this).data('realName');
            if (realName == undefined) {
                realName = $(this).attr("name");
                $(this).data('realName', realName);
            }

            wlog("Name: " + realName + " (actual: " + $(this).attr("name") + "), val: " + $(this).val() + ". Rules: " + rules[realName]);
            $(this).attr("name", "multivalidate-" + realName + "-" + idCounter);
            if (rules[realName]) {
                $(this).rules("add", rules[realName]);
            }
            idCounter++;
        });
    }
}

function revertGeneratedNames(form, fields) {

    var length = fields.length;

    for (var i=0; i < length; ++i ){
        var name = fields[i];
        wlog("look for fields names [" + name + "]");

        $("form [name^='multivalidate-" + name + "']").each(function() {
            var realName = $(this).data('realName');
            if (realName == undefined) {
                wlog("Error: field named [" + $(this).attr("name") + "] does not have a stored real name");
            } else {
                wlog("Convert [" + $(this).attr("name") + "] back to [" + realName + "]");
                $(this).attr("name", realName);
            }
        });
    }
}

On the form load, and whenever I dynamically add another row, I call the set method, e.g.

setGeneratedNamesWithValidationRules($("#my-dynamic-form"), ['amounts'], { 'amounts': 'required'} );

This changes the names to allow individual validation.

In the submitHandler: thingumy after validation I call the revert, i.e.

revertGeneratedNames(form, ['amounts']);

Which switches the names back to the originals before posting the data.

Alfonzo
  • 86
  • 4
-1

There is a simple solution:

$(document).ready(function() {
   $(".form").each(function() {
      $(this).validate({
         ...
         ,errorContainer: $(".status_mess",this) // use "this" as reference to that instance of form.
         ...
      });
   });
});
Igor
  • 582
  • 4
  • 6
-2

I think you misunderstood the workings of HTML forms. Every form element needs to have an unique name, except multiple checkboxes and buttons that allow you to choose one/multiple options for one data field.

In your case, not only JQuery validation, but also a server-side form validator would fail, because it can't assign the inputs to the data fields. Suppose, you want the user to enter prename, lastname, e-mail-adress, fax (optional) and all your input fields have name="map"

Then you would receive these lists on submit:

map = ['Joe','Doe','joe.doeAThotmail.com','++22 20182238'] //All fields completed
map = ['Joe','Doe','joe.doeAThotmail.com'] //OK, all mandatory fields completed 
map = ['Doe', 'joe.doeAThotmail.com','++22 20182238']//user forgot prename, should yield error

You see that it is impossible to validate this form reliably.

I recommend to revisit the documentation of your perl form handler or adapt it if you wrote it on your own.

Franz
  • 1,324
  • 8
  • 2
  • 13
    While I broadly agree with you, that form variables should have distinct names, it is not a requirement of the HTML standard. Your example is right in its context - it would be silly to assign the same variable names to what are semantically distinct attributes. However in my case, I have a situation where I need to submit 4 values for what are semantically identical variables: Names of upto 4 children for example, where it makes perfect sense both semantically and standards-wise to give identical names and collect them into a server-side array. I trust you will reconsider your comment – Gurunandan Bhat Jun 02 '09 at 17:39
  • 3
    Downvoted because each form element does NOT need to have a unique name. Only Ids need to be unique. And as Ya. Perelman said, there are many situations where it is perfectly semantic to have the same name for multiple elements. – Vincent McNabb Aug 04 '11 at 00:50
  • Backing up the two previous comments - name does not need to be unique. For example, I have an application where each row in a table is saved when it is changed (ajax submission). The "foo" field in each row has a unique id but all have the name ("foo"). There is no problem server side since I just submit the active row, but the validate plugin only validates the first row. (I use a customer validation method, similar to the example for an accordian site on the validate plugin page). In my case the solution is to manually call .valid() on a collection of the elements from the active row. – Adam Nov 28 '11 at 14:42
  • As Vincent was saying as well, only ID has to be unique. Having different IDs, but the same name can help quite a bit in some cases. E.g. suppose that you have a registration page to a relay running competition (which can have e.g. 2-4 participants, depending on the specific competition metadata), you would want all participants to be on the same page for easy filling. Using same name allows you to easily handle such a case. On server side you'd just get a list of values that you'd be able to assign to the corresponding participant. E.g. param name[0] and surname[0] will go to particpants[0]. – Stef Jan 17 '12 at 13:51