Command disabled: revisions

Submitting Multiple Forms At Once

Sure, you should avoid building multiple forms on one page. And no, technically speaking it is not possible to submit more than one form at a time. So, we're gonna cheat. But before we dive into the specifics, first some assumptions:

  • You've got two or more forms and at least one interface element, let's say a button, to submit all forms.
  • You are not afraid to use a javascript library. Actually, a specific library: Prototype.
  • Your forms have unique form elements. Whenever a form element with the same name reappears in a subsequent form, it is overwritten.

We're going to use javascript to collect the fields from all forms and send it to the server through a newly created hidden form.

This is an example containing two forms:

<form action="/guests/find_assets" class="new_guest" id="search_form" method="post">
  <input name="authenticity_token" type="hidden" value="ys42Gc6JdVAaRKukNDV5M+2jPhSkjCfJarSxMScBX9g=" />                             
  <fieldset>
    <legend>Details of Stay</legend>                                                          
    <div id="search_form_container">
      ...stuff ommitted...
    </div>
  </fieldset>
</form>

<form action="/guests" class="new_guest" id="guest_form" method="post">
  <input name="authenticity_token" type="hidden" value="ys42Gc6JdVAaRKukNDV5M+2jPhSkjCfJarSxMScBX9g=" />
  <fieldset style="border:0px solid white;margin-top:0px;padding-top:0px;">
    <div id="accommodation_container">
      ...stuff ommitted...
    </div>

    <div class="formButton">
      <input id="guest_submit" 
             name="commit"              
             type="button" 
             value="Save New Guest"
             onclick="submit_all('/guests',[$('search_form'), $('guest_form')])" />                                     
    </div>  
  </fieldset>

Nothing special, except for the javascript call in the onclick attribute of the button (the input element). The function submit_all takes two arguments:

  1. the form action (usually a url pointing to the script that processes your form)
  2. an array of form objects (the prototype javascript library gives us $('search_form') as a shortcut for document.getElementById('search_form'))

Whenever you click the button, submit_all serializes all forms (using the Prototype library), creates a new, hidden form and submits the combined form fields through the new form. Please note that in this specific example, there are two authenticity_token fields. That's okay, because they contain the same value. Remember though, that only one field will be sent to the server script.

What follows is the javascript code necessary to make it all possible. If you see any strange function calls, please remember that this code relies heavily upon the Prototype library.

/**
 *	Combine all forms in arr_forms into a new, hidden form. Submit the new form.
 *	
 *	@var action {string} - the form action
 *	@var arr_forms {array} - array of form objects 
 */
function submit_all(action, arr_forms) {
  var valid = true;
  var fields = new Hash();
  arr_forms.each(function(f) {   
    fields = fields.merge(f.serialize(true));   
  });
  var form = createForm(action, fields);
  form.submit();  
}

/**
 *	Create a form using a hash of fields (input names and values).
 *	
 *	@var action {string} - the form action
 *	@var fields {hash} - hash of input names and values 
 *	@return {object} - returns a form object 
 */
function createForm(action, fields) {
  var form = document.createElement('form');
  form.style.display = 'none';
  $$('body')[0].appendChild(form);
  form.method = 'POST';
  form.action = action;
  createFormFields(form, fields);
  return form;  
}

/**
 *	Create hidden input fields for a form.
 *	
 *	@var form {object} - the form
 *	@var fields {hash} - hash of input names and values 
 */
function createFormFields(form, fields) {
  fields.each(function(field) {
    createInput(form, field );
  });
}

/**
 *	Add a single hidden input field to a form
 *	
 *	@var form {object} - the form
 *	@var hash_pair {object} - a hash pair of key and value 
 */
function createInput(form, hash_pair) {
  var input = document.createElement('input');
  input.setAttribute('type', 'hidden');
  input.setAttribute('name', hash_pair.key);
  // Rails' check_box form helper uses the same name for a given checkbox and its 
  // accompanying hidden field 
  if (Object.isArray(hash_pair.value)) {
    input.setAttribute('value', hash_pair.value.max());
  } else {
    input.setAttribute('value', hash_pair.value);
  }
  form.appendChild(input);  
}

With LiveValidation

Here's a version of submit_all that addresses the LiveValidation library to validate all forms.

/**
 *	Combine all forms in arr_forms into a new, hidden form. Submit the new form.
 *	
 *	@var action {string} - the form action
 *	@var arr_forms {array} - array of form objects 
 */
function submit_all(action, arr_forms) {
  var valid = true;
  var fields = new Hash();
  arr_forms.each(function(f) {
    lf = LiveValidationForm.instances[f.id];
    if (valid = (LiveValidation.massValidate(lf.fields) && valid) ? true : false) {
      fields = fields.merge(f.serialize(true));      
    }
  });
  if (valid) {
    var form = createForm(action, fields);
    form.submit();  
  }
}

Personal Tools