NIHVIVO-193 Javascript to handle reloading the form after a failed submit due to validation errors. Most but not all cases are working in this version.
This commit is contained in:
parent
9d7590c1bd
commit
be7cea2203
2 changed files with 282 additions and 94 deletions
|
@ -1,131 +1,244 @@
|
||||||
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||||
|
|
||||||
|
/* ADD new property form
|
||||||
|
/* Step 1: Initial step, with choice to select existing or add new secondary individual.
|
||||||
|
* Displays:
|
||||||
|
* - On page load, unless there are validation error messages in the form
|
||||||
|
* - if there are validation error messages in the form, we are returning from a failed
|
||||||
|
* submission, and will go directly to view 2 to display the error messages.
|
||||||
|
* - After cancelling out of step 2
|
||||||
|
*
|
||||||
|
* Step 2: Main data entry step
|
||||||
|
* Displays:
|
||||||
|
* - On page load after an attempted submission that fails validation
|
||||||
|
* - After clicking button or add new link in view 1
|
||||||
|
* Has three view variations:
|
||||||
|
* - Select an existing secondary individual view
|
||||||
|
* - Add new secondary individual view
|
||||||
|
* - Combined view, if we are returning from a failed validation and can't determine
|
||||||
|
* which variant of view 2 we had submitted the form from. Contains the select
|
||||||
|
* existing element plus the add new link.
|
||||||
|
*
|
||||||
|
* EDIT existing property form
|
||||||
|
* Looks like add form step 2 except shows add new link
|
||||||
|
* Has same three view variants as step 2 add form
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
var customForm = {
|
var customForm = {
|
||||||
|
|
||||||
onLoad: function() {
|
onLoad: function() {
|
||||||
|
|
||||||
// Create references to form elements.
|
this.initObjects();
|
||||||
// NB must be done after the elements have been loaded.
|
this.adjustForJs();
|
||||||
|
this.initForm();
|
||||||
|
},
|
||||||
|
|
||||||
this.form = $('#content form'),
|
// On page load, create references within the customForm scope to DOM elements.
|
||||||
this.button = $('#submit'),
|
// NB These must be assigned after the elements have been loaded onto the page.
|
||||||
this.requiredLegend = $('#requiredLegend'),
|
initObjects: function() {
|
||||||
|
|
||||||
|
this.form = $('#content form');
|
||||||
|
this.button = $('#submit');
|
||||||
|
this.requiredLegend = $('#requiredLegend');
|
||||||
|
|
||||||
// These may need to be changed to classes rather than ids, if there are
|
// These may need to be changed to classes rather than ids, if there are
|
||||||
// multiple sets of divs to show/hide during the workflow.
|
// multiple sets of divs to show/hide during the workflow.
|
||||||
this.addNewLink = $('#addNewLink'),
|
this.addNewLink = $('#addNewLink');
|
||||||
this.existing = $('#existing'),
|
this.existing = $('#existing');
|
||||||
this.addNew = $('#new'),
|
this.addNew = $('#new');
|
||||||
this.entry = $('#entry'),
|
this.entry = $('#entry');
|
||||||
this.existingOrNew = $('#existingOrNew'),
|
this.existingOrNew = $('#existingOrNew');
|
||||||
|
|
||||||
this.cancel = this.form.find('.cancel'),
|
this.cancel = this.form.find('.cancel');
|
||||||
this.requiredHints = this.form.find('.requiredHint'),
|
this.requiredHints = this.form.find('.requiredHint');
|
||||||
|
|
||||||
// Read values used to control display
|
// Read values used to control display
|
||||||
this.editType = $("input[name='editType']").val(),
|
this.editType = $("input[name='editType']").val();
|
||||||
this.entryType = $("input[name='entryType']").val().capitalize(),
|
this.entryType = $("input[name='entryType']").val().capitalize();
|
||||||
this.newType = $("input[name='newType']").val().capitalize();
|
this.newType = $("input[name='newType']").val().capitalize();
|
||||||
|
},
|
||||||
|
|
||||||
this.adjustForJs();
|
// On page load, make changes to the non-Javascript version for the Javascript version.
|
||||||
|
// These are features that will NOT CHANGE throughout the workflow of the Javascript version..
|
||||||
|
adjustForJs: function() {
|
||||||
|
|
||||||
if (this.editType == 'add') { //adding a new entry
|
var selectExistingLabel = $('#existing label');
|
||||||
|
selectExistingLabel.html(selectExistingLabel.html().replace(/Select (Existing )?/, ''));
|
||||||
|
|
||||||
|
this.existingOrNew.hide();
|
||||||
|
},
|
||||||
|
|
||||||
|
initForm: function() {
|
||||||
|
|
||||||
|
//Adding a new entry
|
||||||
|
if (this.editType == 'add') {
|
||||||
this.initAddForm();
|
this.initAddForm();
|
||||||
|
// Editing an existing entry
|
||||||
} else { // editing existing entry
|
} else {
|
||||||
this.initEditForm();
|
this.initEditForm();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// On page load, make changes to the non-Javascript version for the Javascript version.
|
/***** ADD form *****/
|
||||||
// These are features that will not change in the Javascript version.
|
|
||||||
adjustForJs: function() {
|
|
||||||
this.existingOrNew.hide();
|
|
||||||
|
|
||||||
var selectExistingLabel = $('#existing label');
|
// Set up add form on page load, or when returning to initial state from step 2
|
||||||
selectExistingLabel.html(selectExistingLabel.html().replace('Select Existing ', ''));
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
// Reset add form to initial state (step 1)
|
|
||||||
// Should only be needed after returning to step 1 from step 2,
|
|
||||||
// but sometimes seems to be needed on page load as well, so call it from initAddForm()
|
|
||||||
resetAddForm: function() {
|
|
||||||
|
|
||||||
// Clear all form data and error messages
|
|
||||||
$('input:text').val('');
|
|
||||||
$('.error').text('');
|
|
||||||
|
|
||||||
// Remove previously bound event handlers
|
|
||||||
this.cancel.unbind('click');
|
|
||||||
this.button.unbind('click');
|
|
||||||
this.addNewLink.unbind('click');
|
|
||||||
|
|
||||||
this.toggleRequiredHints('remove', this.addNew, this.existing);
|
|
||||||
},
|
|
||||||
|
|
||||||
// Set up add form on page load, or when returning to initial state
|
|
||||||
initAddForm: function() {
|
initAddForm: function() {
|
||||||
|
|
||||||
this.resetAddForm();
|
// If there are validation errors on the page, we're returning from
|
||||||
|
// an attempted submission that failed validation, and we need to go
|
||||||
|
// directly to step 2.
|
||||||
|
if (this.findValidationErrors()) {
|
||||||
|
this.doAddFormStep2();
|
||||||
|
} else {
|
||||||
|
this.doAddFormStep1();
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
// Reset add form to initial state (step 1) after cancelling out of step 2
|
||||||
|
resetAddFormToStep1: function() {
|
||||||
|
|
||||||
|
this.resetForm();
|
||||||
|
this.doAddFormStep1();
|
||||||
|
},
|
||||||
|
|
||||||
|
// Set up the add form for step 1
|
||||||
|
doAddFormStep1: function() {
|
||||||
|
|
||||||
// Step 1 of the form
|
|
||||||
this.addNewLink.show();
|
|
||||||
this.existing.show();
|
this.existing.show();
|
||||||
|
this.addNewLink.show();
|
||||||
this.addNew.hide();
|
this.addNew.hide();
|
||||||
this.entry.hide();
|
this.entry.hide();
|
||||||
this.requiredLegend.hide();
|
this.requiredLegend.hide();
|
||||||
this.button.val('Continue');
|
this.button.val('Continue');
|
||||||
|
|
||||||
// Assign event listeners
|
// Assign event listeners
|
||||||
|
//this.button.unbind('click'); // RY *** Don't need this if we've done a reset
|
||||||
// button => step 2a
|
|
||||||
this.button.bind('click', function() {
|
this.button.bind('click', function() {
|
||||||
customForm.entry.show();
|
customForm.doAddFormStep2SelectExisting();
|
||||||
customForm.showFields(customForm.existing);
|
|
||||||
customForm.addNewLink.hide();
|
|
||||||
customForm.requiredLegend.show();
|
|
||||||
|
|
||||||
$(this).val('Create ' + customForm.entryType);
|
|
||||||
$(this).unbind('click');
|
|
||||||
|
|
||||||
customForm.doCancel();
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
// add new link => step 2b
|
//this.addNewLink.unbind('click'); // RY *** Don't need this if we've done a reset
|
||||||
this.addNewLink.bind('click', function() {
|
this.addNewLink.bind('click', function() {
|
||||||
$(this).hide();
|
customForm.doAddFormStep2AddNew();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
// Set up form when returning directly to step 2, such as after validation errors
|
||||||
|
// on the form submission.
|
||||||
|
doAddFormStep2: function() {
|
||||||
|
|
||||||
|
// If possible, determine which view of the form we were on
|
||||||
|
var existingInputs = this.existing.find(':input'),
|
||||||
|
existingInputsLen = existingInputs.length,
|
||||||
|
addNewInputs = this.addNew.find(':input'),
|
||||||
|
addNewInputsLen = addNewInputs.length,
|
||||||
|
input,
|
||||||
|
i,
|
||||||
|
fn = null;
|
||||||
|
|
||||||
|
// If a value was entered in the addNew section, go back to the addNew view
|
||||||
|
for (i = 0; i < addNewInputsLen; i++) {
|
||||||
|
input = $(addNewInputs[i]);
|
||||||
|
if (input.val() != '') {
|
||||||
|
fn = this.doAddFormStep2AddNew;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If a value was selected in the existing section, go back to the existing view
|
||||||
|
if (fn === null) {
|
||||||
|
for (i = 0; i < existingInputsLen; i++) {
|
||||||
|
input = $(existingInputs[i]);
|
||||||
|
if (input.val() != '') {
|
||||||
|
fn = this.doAddFormStep2SelectExisting;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, default to the combined view
|
||||||
|
// (same as view used to edit existing entry)
|
||||||
|
if (fn === null) {
|
||||||
|
fn = this.doAddFormStep2Combined;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn.call();
|
||||||
|
},
|
||||||
|
|
||||||
|
// Step 2: selecting an existing individual
|
||||||
|
doAddFormStep2SelectExisting: function() {
|
||||||
|
|
||||||
|
// NB Use 'customForm' instead of 'this', because 'this'
|
||||||
|
// doesn't reference customForm when called from an event handler.
|
||||||
|
customForm.entry.show();
|
||||||
|
customForm.showFields(customForm.existing);
|
||||||
|
customForm.addNew.hide();
|
||||||
|
customForm.addNewLink.hide();
|
||||||
|
customForm.requiredLegend.show();
|
||||||
|
|
||||||
|
customForm.doButtonForStep2('Create ' + customForm.entryType);
|
||||||
|
customForm.doCancelForStep2();
|
||||||
|
},
|
||||||
|
|
||||||
|
// Step 2: adding a new individual
|
||||||
|
doAddFormStep2AddNew: function() {
|
||||||
|
|
||||||
|
// NB Use customForm instead of 'this', because 'this'
|
||||||
|
// doesn't reference customForm when called from an event handler.
|
||||||
|
customForm.addNewLink.hide();
|
||||||
customForm.existing.hide();
|
customForm.existing.hide();
|
||||||
customForm.showFields(customForm.addNew);
|
customForm.showFields(customForm.addNew);
|
||||||
customForm.entry.show();
|
customForm.entry.show();
|
||||||
customForm.requiredLegend.show();
|
customForm.requiredLegend.show();
|
||||||
|
|
||||||
customForm.button.val('Create ' + customForm.entryType + ' & ' + customForm.newType);
|
customForm.doButtonForStep2('Create ' + customForm.entryType + ' & ' + customForm.newType);
|
||||||
customForm.button.unbind('click');
|
customForm.doCancelForStep2();
|
||||||
|
|
||||||
customForm.doCancel();
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Step 2: combined view, when we are returning from validation errors and we
|
||||||
|
// can't determine which view of the form we had been on.
|
||||||
|
doAddFormStep2Combined: function() {
|
||||||
|
|
||||||
|
this.showCombinedView();
|
||||||
|
this.doAddNewLink();
|
||||||
|
this.doButtonForStep2('Create ' + this.newType);
|
||||||
|
this.doCancelForStep2();
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
/***** Edit form *****/
|
||||||
|
|
||||||
|
// RY Here we need logic for returning from validation errors, as in add form ********
|
||||||
initEditForm: function() {
|
initEditForm: function() {
|
||||||
|
|
||||||
this.showFields(this.existing);
|
this.showCombinedView();
|
||||||
this.addNewLink.show();
|
this.doAddNewLink();
|
||||||
this.addNew.hide();
|
|
||||||
this.entry.show();
|
|
||||||
this.requiredLegend.show();
|
|
||||||
this.button.val('Save Changes');
|
this.button.val('Save Changes');
|
||||||
|
// Cancel just takes us back to the individual page - no event listener needed
|
||||||
|
|
||||||
this.addNewLink.bind('click', function() {
|
},
|
||||||
$(this).hide();
|
|
||||||
customForm.existing.hide();
|
|
||||||
customForm.showFields(customForm.addNew);
|
|
||||||
|
|
||||||
customForm.button.val('Create ' + customForm.newType + ' & Save Changes');
|
/***** Utilities *****/
|
||||||
});
|
|
||||||
|
|
||||||
|
unbindEventListeners: function() {
|
||||||
|
this.cancel.unbind('click');
|
||||||
|
this.button.unbind('click');
|
||||||
|
this.addNewLink.unbind('click');
|
||||||
|
},
|
||||||
|
|
||||||
|
clearFormData: function() {
|
||||||
|
this.form.find('input:text').val('');
|
||||||
|
this.form.find('select').val('');
|
||||||
|
this.form.find('textarea').val('');
|
||||||
|
|
||||||
|
// For now we can remove the error elements. Later we may include them in
|
||||||
|
// the markup, for customized positioning, in which case we will empty them
|
||||||
|
// but not remove them here. See findValidationErrors().
|
||||||
|
//this.form.find('.validationError').text('');
|
||||||
|
this.form.find('.validationError').remove();
|
||||||
},
|
},
|
||||||
|
|
||||||
// Add required hints to required fields in a list of elements.
|
// Add required hints to required fields in a list of elements.
|
||||||
|
@ -138,9 +251,10 @@ var customForm = {
|
||||||
|
|
||||||
var labelText,
|
var labelText,
|
||||||
newLabelText,
|
newLabelText,
|
||||||
requiredHintText = '<span class="requiredHint"> *</span>';
|
requiredHintText = '<span class="requiredHint"> *</span>',
|
||||||
|
numArgs = arguments.length;
|
||||||
|
|
||||||
for (var i = 1; i < arguments.length; i++) {
|
for (var i = 1; i < numArgs; i++) {
|
||||||
arguments[i].find('label.required').each(function() {
|
arguments[i].find('label.required').each(function() {
|
||||||
labelText = $(this).html();
|
labelText = $(this).html();
|
||||||
newLabelText = action == 'add' ? labelText + requiredHintText :
|
newLabelText = action == 'add' ? labelText + requiredHintText :
|
||||||
|
@ -155,13 +269,89 @@ var customForm = {
|
||||||
this.toggleRequiredHints('add', el);
|
this.toggleRequiredHints('add', el);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
hideFields: function(el) {
|
||||||
|
// Clear any input, so if we reshow the element the input won't still be there.
|
||||||
|
el.find('input:text').val('');
|
||||||
|
alert('here');
|
||||||
|
el.hide();
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
doButtonForStep2: function(text) {
|
||||||
|
customForm.button.unbind('click');
|
||||||
|
customForm.button.val(text);
|
||||||
|
},
|
||||||
|
|
||||||
// Add event listener to the cancel link in step 2
|
// Add event listener to the cancel link in step 2
|
||||||
doCancel: function() {
|
doCancelForStep2: function() {
|
||||||
this.cancel.unbind('click');
|
|
||||||
this.cancel.bind('click', function() {
|
customForm.cancel.unbind('click');
|
||||||
customForm.initAddForm();
|
customForm.cancel.bind('click', function() {
|
||||||
|
customForm.resetAddFormToStep1();
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
doAddNewLink: function() {
|
||||||
|
|
||||||
|
this.addNewLink.unbind('click');
|
||||||
|
this.addNewLink.bind('click', function() {
|
||||||
|
$(this).hide();
|
||||||
|
customForm.hideFields(customForm.existing);
|
||||||
|
customForm.showFields(customForm.addNew);
|
||||||
|
|
||||||
|
customForm.button.val('Create ' + customForm.newType + ' & Save Changes');
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
// Return true iff there are validation errors on the form
|
||||||
|
findValidationErrors: function() {
|
||||||
|
|
||||||
|
return this.form.find('.validationError').length > 0;
|
||||||
|
|
||||||
|
// RY For now, we just need to look for the presence of the error elements.
|
||||||
|
// Later, however, we may generate empty error messages in the markup, for
|
||||||
|
// customized positioning, in which case we need to look for whether they have
|
||||||
|
// content. See clearFormData().
|
||||||
|
// var foundErrors = false,
|
||||||
|
// errors = this.form.find('.validationError'),
|
||||||
|
// numErrors = errors.length,
|
||||||
|
// i,
|
||||||
|
// error;
|
||||||
|
//
|
||||||
|
// for (i = 0; foundErrors == false && i < numErrors; i++) {
|
||||||
|
// error = errors[i];
|
||||||
|
// if (error.html() != '') {
|
||||||
|
// foundErrors = true;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// return foundErrors;
|
||||||
|
},
|
||||||
|
|
||||||
|
resetForm: function() {
|
||||||
|
|
||||||
|
// Clear all form data and error messages
|
||||||
|
this.clearFormData();
|
||||||
|
|
||||||
|
// Remove previously bound event handlers
|
||||||
|
this.unbindEventListeners();
|
||||||
|
|
||||||
|
// Remove required field hints
|
||||||
|
this.toggleRequiredHints('remove', this.addNew, this.existing);
|
||||||
|
},
|
||||||
|
|
||||||
|
// This version of the form shows both the existing select and add new link.
|
||||||
|
// Used when loading edit form, and when returning from failed submission
|
||||||
|
// of add form when we can't determine which view was being used to make
|
||||||
|
// the submission.
|
||||||
|
showCombinedView: function() {
|
||||||
|
|
||||||
|
this.showFields(this.existing);
|
||||||
|
this.addNewLink.show();
|
||||||
|
this.addNew.hide();
|
||||||
|
this.entry.show();
|
||||||
|
this.requiredLegend.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,8 +5,6 @@
|
||||||
<%@ taglib uri="http://vitro.mannlib.cornell.edu/vitro/tags/StringProcessorTag" prefix="p" %>
|
<%@ taglib uri="http://vitro.mannlib.cornell.edu/vitro/tags/StringProcessorTag" prefix="p" %>
|
||||||
<jsp:useBean id="now" class="org.joda.time.DateTime"/>
|
<jsp:useBean id="now" class="org.joda.time.DateTime"/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<c:choose>
|
<c:choose>
|
||||||
<c:when test="${!empty individual}"><%-- individual is the OBJECT of the property referenced -- the Position, not the Person or Organization --%>
|
<c:when test="${!empty individual}"><%-- individual is the OBJECT of the property referenced -- the Position, not the Person or Organization --%>
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue