I am trying to create a survey that employees can fill out after completing a phone call that will be used to track what products or services were sold during the interaction. The form can have an infinite number of accounts appended to the account-wrapper and each account can have an infinite number of loans appended to its loan wrapper. I am using Jörn Zaefferer's validation plugin to validate the survey before it is submitted; however, the plugin only validates the account most recently added to the form.
How do I get the plugin to validate the entire form?
What I Have Learned: I am dumb and checkboxes should use REQUIRE and never REQUIRE FROM GROUP
Here is the code I use for my form.
<form id="survey" method="GET" action="/processSurvey">
<h1>What products/services were completed during the call?</h1>
<div id="account-wrapper">
</div><!-- End Account Wrapper -->
<button type="submit">Submit</button> <button type="button" onclick="addAccount();">Add Account</button>
<input type="hidden" name="callID" value="0" />
</form>
I dynamically append accounts to the account-wrapper using the handlebars template below:
<script id="account-template" type="text/x-handlebars-template">
<div id="account-{{id}}" class="account">
<div class="account-title ui-state-active">Account<button type="button">Remove Account</button></div>
<div class="service-wrapper">
<div class="service-container">
<span class="title">Account</span>
{{#each account}}
<span class="service-option">
<input type="checkbox" class="service service-group-{{accountID}}" name="account[{{accountID}}][service][]" value="{{value}}" /> {{html}}
</span>
{{/each}}
</div>
<div class="service-container">
<span class="title">Savings</span>
{{#each savings}}
<span class="service-option">
<input type="checkbox" class="service service-group-{{accountID}}" name="account[{{accountID}}][service][]" value="{{value}}" /> {{html}}
</span>
{{/each}}
</div>
<div class="service-container">
<span class="title">Checking</span>
{{#each checking}}
<span class="service-option">
<input type="checkbox" class="service service-group-{{accountID}}" name="account[{{accountID}}][service][]" value="{{value}}" /> {{html}}
</span>
{{/each}}
</div>
<div class="service-container">
<span class="title">Cards</span>
{{#each cards}}
<span class="service-option">
<input type="checkbox" class="service service-group-{{accountID}}" name="account[{{accountID}}][service][]" value="{{value}}" /> {{html}}
</span>
{{/each}}
</div>
<div class="service-container">
<span class="title">Other</span>
<textarea class="service service-group-{{id}}" name="account[{{id}}][service][]"></textarea>
</div>
<div class="error-container"></div>
</div>
<div id="loan-wrapper-{{id}}" class="loan-wrapper">
<div class="loan-title">Loans<button type="button" onclick="addLoan({{id}});">Add Loan</button></div>
<div class="loan-container"></div>
</div>
</div>
</script>
Entire JavaScript:
$(document).ready(function(){
addAccount();
});
$(":button").button();
var loanCounter = 0;
var accountCounter = 0;
$.validator.addMethod("require_from_group", function(value, element, options) {
var validator = this;
var selector = options[1];
var validOrNot = $(selector, element.form).filter(function() {
return validator.elementValue(this);
}).length >= options[0];
if(!$(element).data('being_validated')) {
var fields = $(selector, element.form);
fields.data('being_validated', true);
fields.valid();
fields.data('being_validated', false);
}
return validOrNot;
}, $.format("Please select at least {0} product/service above (or fill in other)."));
$.validator.addMethod("ignorePlaceholder", function(value,element){
return this.optional(element) || (value !== element.placeholder);
},"This field is required.");
$.validator.addClassRules("required", {
ignorePlaceholder: true,
required: true
});
var validator = $("#survey").validate({
debug: true,
errorClass: "error",
validClass: "valid",
submitHandler: function(form){
form.submit();
},
invalidHandler: function(){
alert(validator.numberOfInvalids());
},
errorPlacement: function(error, element){
if($(element).hasClass("service")){
error.appendTo($(element).parents(".service-wrapper").children("div.error-container"));
}
},
rules: {
}
});
function addAccount(){
var newAccount = {
id:accountCounter,
account:[
{value:"1",html:"New Account",accountID:accountCounter},
{value:"13",html:"eStatements",accountID:accountCounter}
],
savings:[
{value:"2",html:"My Choice",accountID:accountCounter},
{value:"3",html:"Money Market",accountID:accountCounter},
{value:"4",html:"Individual Reitrement Account",accountID:accountCounter},
{value:"5",html:"Individual Retirement Account (Certificate of Deposit)",accountID:accountCounter},
{value:"6",html:"Certificate of Deposit",accountID:accountCounter}
],
checking:[
{value:"7",html:"Prosper Checking",accountID:accountCounter},
{value:"8",html:"ePerks Checking",accountID:accountCounter},
{value:"9",html:"Protect Checking",accountID:accountCounter},
{value:"10",html:"Basic Checking",accountID:accountCounter}
],
cards:[
{value:"11",html:"Credit Card",accountID:accountCounter},
{value:"12",html:"Debit Card",accountID:accountCounter}
]
}
var template = Handlebars.compile($("#account-template").html());
var result = template(newAccount);
$("#account-wrapper").append(result);
$("#account-wrapper .account:last :button").button();
$("#account-wrapper .account:last div.account-title :button").click(function(){
$(this).parents("div.account").remove();
});
$("#account-"+accountCounter).placeholder();
$("html, body").animate({scrollTop: $("#account-"+accountCounter).offset().top},1000);
$(".service-group-"+accountCounter).each(function() {
$(this).rules('add', {
require_from_group: [1, ".service-group-"+accountCounter]
});
});
accountCounter++;
}
Notice how the top form is invalid because I do not have an option checked. If I add an identical account template and check an option, the form will submit.
jsFiddle <-- Link to working replica of the problem. Notice how the form only cares that the most recently added account is valid.