mirror of
https://github.com/silverstripe/silverstripe-userforms.git
synced 2024-10-22 17:05:42 +02:00
Merge pull request #39 from tractorcow/pulls/conditional-steps
Pulls/conditional steps
This commit is contained in:
commit
c0d17506be
@ -16,11 +16,13 @@ class UserFormValidator extends RequiredFields {
|
|||||||
|
|
||||||
// Current nesting
|
// Current nesting
|
||||||
$stack = array();
|
$stack = array();
|
||||||
|
$conditionalStep = false; // Is the current step conditional?
|
||||||
foreach($fields as $field) {
|
foreach($fields as $field) {
|
||||||
if($field instanceof EditableFormStep) {
|
if($field instanceof EditableFormStep) {
|
||||||
// Page at top level, or after another page is ok
|
// Page at top level, or after another page is ok
|
||||||
if(empty($stack) || (count($stack) === 1 && $stack[0] instanceof EditableFormStep)) {
|
if(empty($stack) || (count($stack) === 1 && $stack[0] instanceof EditableFormStep)) {
|
||||||
$stack = array($field);
|
$stack = array($field);
|
||||||
|
$conditionalStep = $field->DisplayRules()->count() > 0;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,6 +103,22 @@ class UserFormValidator extends RequiredFields {
|
|||||||
// Unnest group
|
// Unnest group
|
||||||
array_pop($stack);
|
array_pop($stack);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Normal field type
|
||||||
|
if($conditionalStep && $field->Required) {
|
||||||
|
$this->validationError(
|
||||||
|
'FormFields',
|
||||||
|
_t(
|
||||||
|
"UserFormValidator.CONDITIONAL_REQUIRED",
|
||||||
|
"Required field '{name}' cannot be placed within a conditional page",
|
||||||
|
array(
|
||||||
|
'name' => $field->CMSTitle
|
||||||
|
)
|
||||||
|
),
|
||||||
|
'error'
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -395,7 +395,7 @@ class UserDefinedForm_Controller extends Page_Controller {
|
|||||||
|
|
||||||
// Is this Field Show by Default
|
// Is this Field Show by Default
|
||||||
if(!$field->ShowOnLoad) {
|
if(!$field->ShowOnLoad) {
|
||||||
$default .= "{$holderSelector}.hide();\n";
|
$default .= "{$holderSelector}.hide().trigger('userform.field.hide');\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for field dependencies / default
|
// Check for field dependencies / default
|
||||||
@ -503,16 +503,26 @@ class UserDefinedForm_Controller extends Page_Controller {
|
|||||||
$actions = array();
|
$actions = array();
|
||||||
|
|
||||||
foreach($values as $rule) {
|
foreach($values as $rule) {
|
||||||
// Register conditional behaviour with an element, so it can be triggered from many places.
|
// Assign action
|
||||||
$logic[] = sprintf(
|
|
||||||
'if(%s) { %s.%s(); } else { %2$s.%s(); }',
|
|
||||||
$rule['expression'],
|
|
||||||
$rule['holder_selector'],
|
|
||||||
$rule['view'],
|
|
||||||
$rule['opposite']
|
|
||||||
);
|
|
||||||
|
|
||||||
$actions[$rule['action']] = $rule['action'];
|
$actions[$rule['action']] = $rule['action'];
|
||||||
|
|
||||||
|
// Assign behaviour
|
||||||
|
$expression = $rule['expression'];
|
||||||
|
$holder = $rule['holder_selector'];
|
||||||
|
$view = $rule['view']; // hide or show
|
||||||
|
$opposite = $rule['opposite'];
|
||||||
|
// Generated javascript for triggering visibility
|
||||||
|
$logic[] = <<<"EOS"
|
||||||
|
if({$expression}) {
|
||||||
|
{$holder}
|
||||||
|
.{$view}()
|
||||||
|
.trigger('userform.field.{$view}');
|
||||||
|
} else {
|
||||||
|
{$holder}
|
||||||
|
.{$opposite}()
|
||||||
|
.trigger('userform.field.{$opposite}');
|
||||||
|
}
|
||||||
|
EOS;
|
||||||
}
|
}
|
||||||
|
|
||||||
$logic = implode("\n", $logic);
|
$logic = implode("\n", $logic);
|
||||||
|
@ -554,7 +554,8 @@ class EditableFormField extends DataObject {
|
|||||||
*/
|
*/
|
||||||
public function getFieldValidationOptions() {
|
public function getFieldValidationOptions() {
|
||||||
$fields = new FieldList(
|
$fields = new FieldList(
|
||||||
CheckboxField::create('Required', _t('EditableFormField.REQUIRED', 'Is this field Required?')),
|
CheckboxField::create('Required', _t('EditableFormField.REQUIRED', 'Is this field Required?'))
|
||||||
|
->setDescription(_t('EditableFormField.REQUIRED_DESCRIPTION', 'Please note that conditional fields can\'t be required')),
|
||||||
TextField::create('CustomErrorMessage', _t('EditableFormField.CUSTOMERROR','Custom Error Message'))
|
TextField::create('CustomErrorMessage', _t('EditableFormField.CUSTOMERROR','Custom Error Message'))
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -675,7 +676,7 @@ class EditableFormField extends DataObject {
|
|||||||
!$data[$this->Name] ||
|
!$data[$this->Name] ||
|
||||||
!$formField->validate($form->getValidator())
|
!$formField->validate($form->getValidator())
|
||||||
) {
|
) {
|
||||||
$form->addErrorMessage($this->Name, $this->getErrorMessage()->HTML(), 'bad', false);
|
$form->addErrorMessage($this->Name, $this->getErrorMessage()->HTML(), 'error', false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -779,4 +780,12 @@ class EditableFormField extends DataObject {
|
|||||||
asort($editableFieldClasses);
|
asort($editableFieldClasses);
|
||||||
return $editableFieldClasses;
|
return $editableFieldClasses;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return EditableFormFieldValidator
|
||||||
|
*/
|
||||||
|
public function getCMSValidator() {
|
||||||
|
return EditableFormFieldValidator::create()
|
||||||
|
->setRecord($this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
61
code/model/editableformfields/EditableFormFieldValidator.php
Normal file
61
code/model/editableformfields/EditableFormFieldValidator.php
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
class EditableFormFieldValidator extends RequiredFields {
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @var EditableFormField
|
||||||
|
*/
|
||||||
|
protected $record = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param EditableFormField $record
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setRecord($record) {
|
||||||
|
$this->record = $record;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @return EditableFormField
|
||||||
|
*/
|
||||||
|
public function getRecord() {
|
||||||
|
return $this->record;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function php($data) {
|
||||||
|
if(!parent::php($data)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip unsaved records
|
||||||
|
if(!$this->record || !$this->record->exists()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip validation if not required
|
||||||
|
if(empty($data['Required'])) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip validation if no rules
|
||||||
|
$count = EditableCustomRule::get()->filter('ParentID', $this->record->ID)->count();
|
||||||
|
if($count == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Both required = true and rules > 0 should error
|
||||||
|
$this->validationError(
|
||||||
|
'Required_Error',
|
||||||
|
_t(
|
||||||
|
"EditableFormFieldValidator.REQUIRED_ERROR",
|
||||||
|
"Form fields cannot be required and have conditional display rules."
|
||||||
|
),
|
||||||
|
'error'
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
@ -24,7 +24,7 @@ class EditableFormStep extends EditableFormField {
|
|||||||
public function getCMSFields() {
|
public function getCMSFields() {
|
||||||
$fields = parent::getCMSFields();
|
$fields = parent::getCMSFields();
|
||||||
|
|
||||||
$fields->removeByName(array('MergeField', 'Default', 'Validation', 'DisplayRules'));
|
$fields->removeByName(array('MergeField', 'Default', 'Validation'));
|
||||||
|
|
||||||
return $fields;
|
return $fields;
|
||||||
}
|
}
|
||||||
@ -74,4 +74,13 @@ class EditableFormStep extends EditableFormField {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the JS expression for selecting the holder for this field
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getSelectorHolder() {
|
||||||
|
return "$(\".step-button-wrapper[data-for='{$this->Name}']\")";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -44,10 +44,10 @@ jQuery(function ($) {
|
|||||||
this.errorContainer = new ErrorContainer(this.$el.children('.error-container'));
|
this.errorContainer = new ErrorContainer(this.$el.children('.error-container'));
|
||||||
|
|
||||||
// Listen for events triggered my form steps.
|
// Listen for events triggered my form steps.
|
||||||
this.$el.on('userform.step.prev', function (e) {
|
this.$el.on('userform.action.prev', function (e) {
|
||||||
self.prevStep();
|
self.prevStep();
|
||||||
});
|
});
|
||||||
this.$el.on('userform.step.next', function (e) {
|
this.$el.on('userform.action.next', function (e) {
|
||||||
self.nextStep();
|
self.nextStep();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -88,12 +88,14 @@ jQuery(function ($) {
|
|||||||
submitHandler: function (form, e) {
|
submitHandler: function (form, e) {
|
||||||
var isValid = true;
|
var isValid = true;
|
||||||
|
|
||||||
// Validate the final step.
|
// validate the current step
|
||||||
userform.steps[userform.steps.length - 1].valid = $(form).valid();
|
if(userform.currentStep) {
|
||||||
|
userform.currentStep.valid = $(form).valid();
|
||||||
|
}
|
||||||
|
|
||||||
// Check for invalid previous steps.
|
// Check for invalid previous steps.
|
||||||
$.each(userform.steps, function (i, step) {
|
$.each(userform.steps, function (i, step) {
|
||||||
if (!step.valid) {
|
if (!step.valid && !step.conditionallyHidden()) {
|
||||||
isValid = false;
|
isValid = false;
|
||||||
userform.errorContainer.addStepLink(step);
|
userform.errorContainer.addStepLink(step);
|
||||||
}
|
}
|
||||||
@ -154,17 +156,31 @@ jQuery(function ($) {
|
|||||||
/**
|
/**
|
||||||
* @func UserForm.jumpToStep
|
* @func UserForm.jumpToStep
|
||||||
* @param {number} stepNumber
|
* @param {number} stepNumber
|
||||||
|
* @param {boolean} [direction] - Defaults to forward (true).
|
||||||
* @desc Jumps to a specific form step.
|
* @desc Jumps to a specific form step.
|
||||||
*/
|
*/
|
||||||
UserForm.prototype.jumpToStep = function (stepNumber) {
|
UserForm.prototype.jumpToStep = function (stepNumber, direction) {
|
||||||
var targetStep = this.steps[stepNumber],
|
var targetStep = this.steps[stepNumber],
|
||||||
isValid = false;
|
isValid = false,
|
||||||
|
forward = direction === void 0 ? true : direction;
|
||||||
|
|
||||||
// Make sure the target step exists.
|
// Make sure the target step exists.
|
||||||
if (targetStep === void 0) {
|
if (targetStep === void 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Make sure the step we're trying to set as current is not
|
||||||
|
// hidden by custom display rules. If it is then jump to the next step.
|
||||||
|
if (targetStep.conditionallyHidden()) {
|
||||||
|
if (forward) {
|
||||||
|
this.jumpToStep(stepNumber + 1);
|
||||||
|
} else {
|
||||||
|
this.jumpToStep(stepNumber - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Validate the form.
|
// Validate the form.
|
||||||
// This well effectivly validate the current step and not the entire form.
|
// This well effectivly validate the current step and not the entire form.
|
||||||
// This is because hidden fields are excluded from validation, and all fields
|
// This is because hidden fields are excluded from validation, and all fields
|
||||||
@ -172,7 +188,7 @@ jQuery(function ($) {
|
|||||||
isValid = this.$el.valid();
|
isValid = this.$el.valid();
|
||||||
|
|
||||||
// Set the 'valid' property on the current step.
|
// Set the 'valid' property on the current step.
|
||||||
this.steps[stepNumber - 1 >= 0 ? stepNumber - 1 : 0].valid = isValid;
|
this.currentStep.valid = isValid;
|
||||||
|
|
||||||
// Users can navigate to step's they've already viewed even if the current step is invalid.
|
// Users can navigate to step's they've already viewed even if the current step is invalid.
|
||||||
if (isValid === false && targetStep.viewed === false) {
|
if (isValid === false && targetStep.viewed === false) {
|
||||||
@ -182,7 +198,7 @@ jQuery(function ($) {
|
|||||||
this.currentStep.hide();
|
this.currentStep.hide();
|
||||||
this.setCurrentStep(targetStep);
|
this.setCurrentStep(targetStep);
|
||||||
|
|
||||||
this.$el.trigger('userform.form.changestep', [stepNumber]);
|
this.$el.trigger('userform.form.changestep', [targetStep.id]);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -190,7 +206,7 @@ jQuery(function ($) {
|
|||||||
* @desc Advances the form to the next step.
|
* @desc Advances the form to the next step.
|
||||||
*/
|
*/
|
||||||
UserForm.prototype.nextStep = function () {
|
UserForm.prototype.nextStep = function () {
|
||||||
this.jumpToStep(this.steps.indexOf(this.currentStep) + 1);
|
this.jumpToStep(this.steps.indexOf(this.currentStep) + 1, true);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -198,7 +214,7 @@ jQuery(function ($) {
|
|||||||
* @desc Goes back one step (not bound to browser history).
|
* @desc Goes back one step (not bound to browser history).
|
||||||
*/
|
*/
|
||||||
UserForm.prototype.prevStep = function () {
|
UserForm.prototype.prevStep = function () {
|
||||||
this.jumpToStep(this.steps.indexOf(this.currentStep) - 1);
|
this.jumpToStep(this.steps.indexOf(this.currentStep) - 1, false);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -349,7 +365,10 @@ jQuery(function ($) {
|
|||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
this.$el = element instanceof jQuery ? element : $(element);
|
this.$el = element instanceof jQuery ? element : $(element);
|
||||||
|
|
||||||
|
// Find button for this step
|
||||||
|
this.$elButton = $(".step-button-wrapper[data-for='" + this.$el.prop('id') + "']");
|
||||||
|
|
||||||
// Has the step been viewed by the user?
|
// Has the step been viewed by the user?
|
||||||
this.viewed = false;
|
this.viewed = false;
|
||||||
|
|
||||||
@ -362,16 +381,6 @@ jQuery(function ($) {
|
|||||||
|
|
||||||
this.hide();
|
this.hide();
|
||||||
|
|
||||||
// Bind the step navigation event listeners.
|
|
||||||
this.$el.find('.step-button-prev').on('click', function (e) {
|
|
||||||
e.preventDefault();
|
|
||||||
self.$el.trigger('userform.step.prev');
|
|
||||||
});
|
|
||||||
this.$el.find('.step-button-next').on('click', function (e) {
|
|
||||||
e.preventDefault();
|
|
||||||
self.$el.trigger('userform.step.next');
|
|
||||||
});
|
|
||||||
|
|
||||||
if (CONSTANTS.DISPLAY_ERROR_MESSAGES_AT_TOP) {
|
if (CONSTANTS.DISPLAY_ERROR_MESSAGES_AT_TOP) {
|
||||||
this.errorContainer = new ErrorContainer(this.$el.find('.error-container'));
|
this.errorContainer = new ErrorContainer(this.$el.find('.error-container'));
|
||||||
|
|
||||||
@ -394,8 +403,28 @@ jQuery(function ($) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ensure that page visibilty updates the step navigation
|
||||||
|
this
|
||||||
|
.$elButton
|
||||||
|
.on('userform.field.hide userform.field.show', function(){
|
||||||
|
userform.$el.trigger('userform.form.conditionalstep');
|
||||||
|
});
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if this step is conditionally disabled
|
||||||
|
*
|
||||||
|
* @returns {Boolean}
|
||||||
|
*/
|
||||||
|
FormStep.prototype.conditionallyHidden = function(){
|
||||||
|
// Because the element itself could be visible but 0 height, so check visibility of button
|
||||||
|
return ! this
|
||||||
|
.$elButton
|
||||||
|
.find('button')
|
||||||
|
.is(':visible');
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @func ProgressBar
|
* @func ProgressBar
|
||||||
@ -420,8 +449,26 @@ jQuery(function ($) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Update the progress bar when 'prev' and 'next' buttons are clicked.
|
// Update the progress bar when 'prev' and 'next' buttons are clicked.
|
||||||
userform.$el.on('userform.form.changestep', function (e, newStep) {
|
userform.$el.on('userform.form.changestep', function (e, stepID) {
|
||||||
self.update(newStep + 1);
|
self.update(stepID);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Listen for steps being conditionally shown / hidden by display rules.
|
||||||
|
// We need to update step related UI like the number of step buttons
|
||||||
|
// and any text that shows the total number of steps.
|
||||||
|
userform.$el.on('userform.form.conditionalstep', function () {
|
||||||
|
// Update the step numbers on the buttons.
|
||||||
|
var $visibleButtons = self.$buttons.filter(':visible');
|
||||||
|
|
||||||
|
$visibleButtons.each(function (i, button) {
|
||||||
|
$(button).text(i + 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update the actual progress bar.
|
||||||
|
self.$el.find('.progress-bar').attr('aria-valuemax', $visibleButtons.length);
|
||||||
|
|
||||||
|
// Update any text that uses the total number of steps.
|
||||||
|
self.$el.find('.total-step-number').text($visibleButtons.length);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Spaces out the steps below progress bar evenly
|
// Spaces out the steps below progress bar evenly
|
||||||
@ -440,27 +487,39 @@ jQuery(function ($) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.update(1);
|
this.update(0);
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @func ProgressBar.update
|
* @func ProgressBar.update
|
||||||
* @param {number} newStep
|
* @param {number} stepID - Zero based index of the new step.
|
||||||
* @desc Update the progress element to show a new step.
|
* @desc Update the progress element to show a new step.
|
||||||
*/
|
*/
|
||||||
ProgressBar.prototype.update = function (newStep) {
|
ProgressBar.prototype.update = function (stepID) {
|
||||||
var $newStepElement = $($('.form-step')[newStep - 1]);
|
var $newStepElement = $($('.form-step')[stepID]),
|
||||||
|
stepNumber = 0;
|
||||||
|
|
||||||
|
// Set the current step number.
|
||||||
|
this.$buttons.each(function (i, button) {
|
||||||
|
if (i > stepID) {
|
||||||
|
return false; // break the loop
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($(button).is(':visible')) {
|
||||||
|
stepNumber += 1;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Update elements that contain the current step number.
|
// Update elements that contain the current step number.
|
||||||
this.$el.find('.current-step-number').each(function (i, element) {
|
this.$el.find('.current-step-number').each(function (i, element) {
|
||||||
$(element).text(newStep);
|
$(element).text(stepNumber);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Update aria attributes.
|
// Update aria attributes.
|
||||||
this.$el.find('[aria-valuenow]').each(function (i, element) {
|
this.$el.find('[aria-valuenow]').each(function (i, element) {
|
||||||
$(element).attr('aria-valuenow', newStep);
|
$(element).attr('aria-valuenow', stepNumber);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Update the CSS classes on step buttons.
|
// Update the CSS classes on step buttons.
|
||||||
@ -468,7 +527,7 @@ jQuery(function ($) {
|
|||||||
var $element = $(element),
|
var $element = $(element),
|
||||||
$item = $element.parent();
|
$item = $element.parent();
|
||||||
|
|
||||||
if (parseInt($element.text(), 10) === newStep) {
|
if (parseInt($element.text(), 10) === stepNumber && $element.is(':visible')) {
|
||||||
$item.addClass('current viewed');
|
$item.addClass('current viewed');
|
||||||
$element.removeAttr('disabled');
|
$element.removeAttr('disabled');
|
||||||
|
|
||||||
@ -482,7 +541,72 @@ jQuery(function ($) {
|
|||||||
this.$el.find('.progress-title').text($newStepElement.data('title'));
|
this.$el.find('.progress-title').text($newStepElement.data('title'));
|
||||||
|
|
||||||
// Update the width of the progress bar.
|
// Update the width of the progress bar.
|
||||||
this.$el.find('.progress-bar').width((newStep - 1) / (this.$buttons.length - 1) * 100 + '%');
|
this.$el.find('.progress-bar').width(stepID / (this.$buttons.length - 1) * 100 + '%');
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @func FormActions
|
||||||
|
* @constructor
|
||||||
|
* @param {object} element
|
||||||
|
* @desc Creates the navigation and actions (Prev, Next, Submit buttons).
|
||||||
|
*/
|
||||||
|
function FormActions (element) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
this.$el = element instanceof jQuery ? element : $(element);
|
||||||
|
|
||||||
|
// Bind the step navigation event listeners.
|
||||||
|
this.$el.find('.step-button-prev').on('click', function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
self.$el.trigger('userform.action.prev');
|
||||||
|
});
|
||||||
|
this.$el.find('.step-button-next').on('click', function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
self.$el.trigger('userform.action.next');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Listen for changes to the current form step, or conditional pages,
|
||||||
|
// so we can show hide buttons appropriatly.
|
||||||
|
userform.$el.on('userform.form.changestep userform.form.conditionalstep', function () {
|
||||||
|
self.update();
|
||||||
|
});
|
||||||
|
|
||||||
|
this.update();
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @func FormAcrions.update
|
||||||
|
* @param {number} stepID - Zero based ID of the current step.
|
||||||
|
* @desc Updates the form actions element to reflect the current state of the page.
|
||||||
|
*/
|
||||||
|
FormActions.prototype.update = function () {
|
||||||
|
var numberOfSteps = userform.steps.length,
|
||||||
|
stepID = userform.currentStep.id,
|
||||||
|
i, lastStep;
|
||||||
|
|
||||||
|
// Update the "Prev" button.
|
||||||
|
this.$el.find('.step-button-prev')[stepID === 0 ? 'hide' : 'show']();
|
||||||
|
|
||||||
|
// Find last step, skipping hidden ones
|
||||||
|
for(i = numberOfSteps - 1; i >= 0; i--) {
|
||||||
|
lastStep = userform.steps[i];
|
||||||
|
|
||||||
|
// Skip if step is hidden
|
||||||
|
if(lastStep.conditionallyHidden()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the "Next" button.
|
||||||
|
this.$el.find('.step-button-next')[stepID >= i ? 'hide' : 'show']();
|
||||||
|
|
||||||
|
// Update the "Actions".
|
||||||
|
this.$el.find('.Actions')[stepID >= i ? 'show' : 'hide']();
|
||||||
|
|
||||||
|
// Stop processing last step
|
||||||
|
break;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -491,6 +615,7 @@ jQuery(function ($) {
|
|||||||
*/
|
*/
|
||||||
function main() {
|
function main() {
|
||||||
var progressBar = null,
|
var progressBar = null,
|
||||||
|
formActions = null,
|
||||||
$userform = $('.userform');
|
$userform = $('.userform');
|
||||||
|
|
||||||
CONSTANTS.ENABLE_LIVE_VALIDATION = $userform.data('livevalidation') !== void 0;
|
CONSTANTS.ENABLE_LIVE_VALIDATION = $userform.data('livevalidation') !== void 0;
|
||||||
@ -523,7 +648,6 @@ jQuery(function ($) {
|
|||||||
$.extend(ErrorContainer.prototype, commonMixin);
|
$.extend(ErrorContainer.prototype, commonMixin);
|
||||||
|
|
||||||
userform = new UserForm($userform);
|
userform = new UserForm($userform);
|
||||||
progressBar = new ProgressBar($('#userform-progress'));
|
|
||||||
|
|
||||||
// Conditionally hide field labels and use HTML5 placeholder instead.
|
// Conditionally hide field labels and use HTML5 placeholder instead.
|
||||||
if (CONSTANTS.HIDE_FIELD_LABELS) {
|
if (CONSTANTS.HIDE_FIELD_LABELS) {
|
||||||
@ -543,6 +667,10 @@ jQuery(function ($) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
userform.setCurrentStep(userform.steps[0]);
|
userform.setCurrentStep(userform.steps[0]);
|
||||||
|
|
||||||
|
// Initialise actions and progressbar
|
||||||
|
progressBar = new ProgressBar($('#userform-progress'));
|
||||||
|
formActions = new FormActions($('#step-navigation'));
|
||||||
|
|
||||||
// Hide the form-wide actions on multi-step forms.
|
// Hide the form-wide actions on multi-step forms.
|
||||||
// Because JavaScript is enabled we'll use the actions contained
|
// Because JavaScript is enabled we'll use the actions contained
|
||||||
|
@ -1,16 +1,17 @@
|
|||||||
<% if $Steps.Count > 1 %>
|
<% if $Steps.Count > 1 %>
|
||||||
<div id="userform-progress" class="userform-progress" aria-hidden="true" style="display:none;">
|
<div id="userform-progress" class="userform-progress" aria-hidden="true" style="display:none;">
|
||||||
<h2 class="progress-title"></h2>
|
<h2 class="progress-title"></h2>
|
||||||
<p>Step <span class="current-step-number">1</span> of $NumberOfSteps.Count</p>
|
<p>Step <span class="current-step-number">1</span> of <span class="total-step-number">$Steps.Count</span></p>
|
||||||
<div class="progress">
|
<div class="progress">
|
||||||
<div class="progress-bar" role="progressbar" aria-valuenow="1" aria-valuemin="1" aria-valuemax="$NumberOfSteps.Count"></div>
|
<div class="progress-bar" role="progressbar" aria-valuenow="1" aria-valuemin="1" aria-valuemax="$Steps.Count"></div>
|
||||||
</div>
|
</div>
|
||||||
<nav>
|
<nav>
|
||||||
<ul class="step-buttons">
|
<ul class="step-buttons">
|
||||||
<% loop $Steps %>
|
<% loop $Steps %>
|
||||||
<li class="step-button-wrapper<% if $First %> current<% end_if %>">
|
<li class="step-button-wrapper<% if $First %> current<% end_if %>" data-for="$Name">
|
||||||
<button class="step-button-jump js-align" disabled="disabled">$Pos</button><!-- Remove js-align class to remove javascript positioning -->
|
<%-- Remove js-align class to remove javascript positioning --%>
|
||||||
</li>
|
<button class="step-button-jump js-align" disabled="disabled">$Pos</button>
|
||||||
|
</li>
|
||||||
<% end_loop %>
|
<% end_loop %>
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
|
@ -1,21 +1,17 @@
|
|||||||
<% if $Form.Steps.Count > 1 %>
|
<nav id="step-navigation" class="step-navigation" aria-hidden="true" style="display:none;">
|
||||||
<nav class="step-navigation" aria-hidden="true" style="display:none;">
|
<ul class="step-buttons">
|
||||||
<ul class="step-buttons">
|
<li class="step-button-wrapper">
|
||||||
<% if $StepNumber > 1 %>
|
<button class="step-button-prev">Prev</button>
|
||||||
<li class="step-button-wrapper">
|
</li>
|
||||||
<button class="step-button-prev">Prev</button>
|
<li class="step-button-wrapper">
|
||||||
</li>
|
<button class="step-button-next">Next</button>
|
||||||
<% end_if %>
|
</li>
|
||||||
|
<% if $Actions %>
|
||||||
<% if $StepNumber < $Form.Steps.Count %>
|
<li class="step-button-wrapper Actions">
|
||||||
<li class="step-button-wrapper">
|
<% loop $Actions %>
|
||||||
<button class="step-button-next">Next</button>
|
$Field
|
||||||
</li>
|
<% end_loop %>
|
||||||
<% else_if $Form.Actions %><% loop $Form.Actions %>
|
</li>
|
||||||
<li class="step-button-wrapper">
|
<% end_if %>
|
||||||
$Field
|
</ul>
|
||||||
</li>
|
</nav>
|
||||||
<% end_loop %><% end_if %>
|
|
||||||
</ul>
|
|
||||||
</nav>
|
|
||||||
<% end_if %>
|
|
||||||
|
@ -17,6 +17,22 @@
|
|||||||
<div class="clear"><!-- --></div>
|
<div class="clear"><!-- --></div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
|
<%--
|
||||||
|
Include step navigation if it's a multi-page form.
|
||||||
|
The markup inside this include is hidden by default and displayed if JavaScript is enabled.
|
||||||
|
--%>
|
||||||
|
|
||||||
|
<% if $Steps.Count > 1 %>
|
||||||
|
<% include UserFormStepNav %>
|
||||||
|
<% end_if %>
|
||||||
|
|
||||||
|
<%--
|
||||||
|
When JavaScript is disabled, multi-page forms are diaplayed as a single page form,
|
||||||
|
and these actions are used instead of the step navigation include.
|
||||||
|
|
||||||
|
These actions are hidden by JavaScript on multi-page forms.
|
||||||
|
--%>
|
||||||
|
|
||||||
<% if $Actions %>
|
<% if $Actions %>
|
||||||
<div class="Actions">
|
<div class="Actions">
|
||||||
<% loop $Actions %>
|
<% loop $Actions %>
|
||||||
|
@ -7,10 +7,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<% end_if %>
|
<% end_if %>
|
||||||
|
|
||||||
<% loop $Children %>
|
<% loop $Children %>
|
||||||
$FieldHolder
|
$FieldHolder
|
||||||
<% end_loop %>
|
<% end_loop %>
|
||||||
|
|
||||||
<% include UserFormStepNav %>
|
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
Loading…
Reference in New Issue
Block a user