Using JQuery Validate Plugin to validate multiple form fields with identical names

JqueryValidationJquery Validate

Jquery Problem Overview


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][1] 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.

[1]: http://docs.jquery.com/Plugins/Validation "Validation"

Jquery Solutions


Solution 1 - Jquery

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.

Solution 2 - Jquery

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

Solution 3 - Jquery

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



			
	
});

Solution 4 - Jquery

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.

Solution 5 - Jquery

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

Solution 6 - Jquery

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!

Solution 7 - Jquery

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

});

Solution 8 - Jquery

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

Solution 9 - Jquery

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.

Solution 10 - Jquery

For me this was solved very easily by disabling the debug

 $("#_form").validate({
    debug:false,
    //debug: true,
    ...
    });

Solution 11 - Jquery

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

Solution 12 - Jquery

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.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionYa. PerelmanView Question on Stackoverflow
Solution 1 - JqueryAhmed GalalView Answer on Stackoverflow
Solution 2 - JqueryscampbellView Answer on Stackoverflow
Solution 3 - JqueryjasonView Answer on Stackoverflow
Solution 4 - JqueryYa. PerelmanView Answer on Stackoverflow
Solution 5 - JquerySteve FarthingView Answer on Stackoverflow
Solution 6 - JqueryMatt KircherView Answer on Stackoverflow
Solution 7 - JqueryMotlicek PetrView Answer on Stackoverflow
Solution 8 - JqueryStefView Answer on Stackoverflow
Solution 9 - JqueryAlfonzoView Answer on Stackoverflow
Solution 10 - JqueryEbrahimView Answer on Stackoverflow
Solution 11 - JqueryIgorView Answer on Stackoverflow
Solution 12 - JqueryFranzView Answer on Stackoverflow