From 97eb8b025545eaaaa7ab8b639f3acc60e836b13c Mon Sep 17 00:00:00 2001 From: David Craig Date: Thu, 6 Aug 2015 17:47:14 +1200 Subject: [PATCH] Initial validation --- javascript/UserForm.js | 110 +++++++++++++++++++++++-- templates/Includes/UserFormProgress.ss | 2 +- 2 files changed, 102 insertions(+), 10 deletions(-) diff --git a/javascript/UserForm.js b/javascript/UserForm.js index dad383a..28d88aa 100644 --- a/javascript/UserForm.js +++ b/javascript/UserForm.js @@ -3,6 +3,15 @@ */ jQuery(function ($) { + // Settings that come from the CMS. + var CONSTANTS = { + ERROR_CONTAINER_ID: '', + ENABLE_LIVE_VALIDATION: false, + DISPLAY_ERROR_MESSAGES_AT_TOP: false, + HIDE_FIELD_LABELS: false, + MESSAGES: {} + }; + /** * @func UserForm * @constructor @@ -59,6 +68,10 @@ jQuery(function ($) { this.currentStep = step; this.currentStep.show(); + + // Record the user has viewed the step. + step.viewed = true; + step.$el.addClass('viewed'); }; /** @@ -74,6 +87,13 @@ jQuery(function ($) { return; } + // Validate the current section. + // Users can navigate to step's they've already viewed + // even if the current step is invalid. + if (!this.$el.valid() && targetStep.viewed === false) { + return; + } + this.currentStep.hide(); this.setCurrentStep(targetStep); @@ -108,6 +128,11 @@ jQuery(function ($) { this.$el = element instanceof jQuery ? element : $(element); + // Has the step been viewed by the user? + this.viewed = false; + + this.hide(); + // Bind the step navigation event listeners. this.$el.find('.step-button-prev').on('click', function (e) { e.preventDefault(); @@ -118,11 +143,50 @@ jQuery(function ($) { self.$el.trigger('userform.step.next'); }); - this.hide(); + // Set up validation for the step. + this.$el.validate(this.validationOptions); return this; } + /* + * Default options for step validation. These get extended in main(). + */ + FormStep.prototype.validationOptions = { + ignore: ':hidden', + errorClass: 'required', + errorElement: 'span', + errorPlacement: function (error, element) { + error.addClass('message'); + + if(element.is(':radio') || element.parents('.checkboxset').length > 0) { + error.insertAfter(element.closest('ul')); + } else { + error.insertAfter(element); + } + + if (CONSTANTS.DISPLAY_ERROR_MESSAGES_AT_TOP) { + // TODO + //applyTopErrorMessage(element, error.html()); + } + + }, + success: function (error) { + error.remove(); + }, + messages: CONSTANTS.MESSAGES, + rules: { + // TODO + // <% loop $Fields %> + // <% if $Validation %><% if ClassName == EditableCheckboxGroupField %> + // '{$Name.JS}[]': {$ValidationJSON.RAW}, + // <% else %> + // '{$Name.JS}': {$ValidationJSON.RAW}, + // <% end_if %><% end_if %> + // <% end_loop %> + } + }; + /** * @func FormStep.show * @desc Show the form step. Looks after aria attributes too. @@ -155,12 +219,8 @@ jQuery(function ($) { // Update the progress bar when 'step' buttons are clicked. this.$buttons.each(function (i, stepButton) { $(stepButton).on('click', function (e) { - var newStepNumber = parseInt($(this).text(), 10); - e.preventDefault(); - - self.update(newStepNumber); - self.$el.trigger('userform.progress.changestep', [newStepNumber]); + self.$el.trigger('userform.progress.changestep', [parseInt($(this).text(), 10)]); }); }); @@ -192,10 +252,13 @@ jQuery(function ($) { // Update the CSS classes on step buttons. this.$buttons.each(function (i, element) { - var $item = $(element).parent(); + var $element = $(element), + $item = $element.parent(); + + if (parseInt($element.text(), 10) === newStep) { + $item.addClass('current viewed'); + $element.removeAttr('disabled'); - if (parseInt($(element).text(), 10) === newStep) { - $item.addClass('current'); return; } @@ -214,6 +277,35 @@ jQuery(function ($) { var userform = new UserForm($('.userform')), progressBar = new ProgressBar($('#userform-progress')); + // Extend the default validation options with conditional options + // that are set by the user in the CMS. + if (CONSTANTS.ENABLE_LIVE_VALIDATION) { + $.extend(FormStep.prototype.validationOptions, { + onfocusout: function (element) { + this.element(element); + } + }); + } + + if (CONSTANTS.DISPLAY_ERROR_MESSAGES_AT_TOP) { + $.extend(FormStep.prototype.validationOptions, { + invalidHandler: function (event, validator) { + var errorList = $('#' + CONSTANTS.ERROR_CONTAINER_ID + ' ul'); + + // Update the error list with errors from the validator. + // We do this because top messages are not part of the regular + // error message life cycle, which jquery.validate handles for us. + errorList.empty(); + + $.each(validator.errorList, function () { + // TODO + //applyTopErrorMessage($(this.element), this.message); + }); + }, + onfocusout: false + }); + } + // Display all the things that are hidden when JavaScript is disabled. $.each(['#userform-progress', '.step-navigation'], function (i, selector) { $(selector).attr('aria-hidden', false).show(); diff --git a/templates/Includes/UserFormProgress.ss b/templates/Includes/UserFormProgress.ss index 4fbe80c..8c1e384 100644 --- a/templates/Includes/UserFormProgress.ss +++ b/templates/Includes/UserFormProgress.ss @@ -9,7 +9,7 @@