Modified the polls view for an event to show not only the list of polls but the options as well as the min and max selections. Updated the updateForm() JS method to update the IDs of the form inputs for each polls so that they're in sync with the poll count. This completes the front-end JS validation and the create event form.

This commit is contained in:
vince0656 2018-06-20 17:36:22 +01:00
parent dd3fdbd41e
commit 8210ef6995
4 changed files with 117 additions and 69 deletions

View File

@ -45,8 +45,8 @@ class CreateNewEventModelAdaptor:
self.form_data = form_data.copy()
self.user = user
# TODO: Call validation func here (incl functionality for verifying CSRF + reCAPTCHA)
print("Form Data:")
print(self.form_data)
#print("Form Data:")
#print(self.form_data)
self.__extractData()

View File

@ -44,7 +44,7 @@
<div class="form-group">
<label for="name-input" class="col-sm-3 col-md-2 control-label">Name:</label> <!-- This text can be a template variable -->
<div class="col-sm-9 col-md-10">
<input type="text" class="form-control input-control" id="name-input" placeholder="Example: My poll" name="name-input" maxlength="255">
<input type="text" class="form-control input-control" id="name-input" placeholder="Example: EU Election" name="name-input" maxlength="255">
<span id="name-input-hint-block" class="help-block">
A short and clear name.
</span>
@ -57,7 +57,7 @@
<div class="form-group">
<label for="identifier-input" class="col-sm-3 col-md-2 control-label">Identifier:</label> <!-- This text can be a template variable -->
<div class="col-sm-9 col-md-10">
<input type="text" class="form-control input-control" id="identifier-input" placeholder="Example: My-poll" name="identifier-input" maxlength="255">
<input type="text" class="form-control input-control" id="identifier-input" placeholder="Example: eu-election" name="identifier-input" maxlength="255">
<span id="identifier-input-help-block" class="help-block">
Used in the election URL, it must only consist of letters, numbers, underscores or hyphens; no whitespace is permitted.
</span>

View File

@ -4,17 +4,25 @@
{% block event_nav_polls %}active{% endblock %}
{% block event_content %}
<h2>Related Polls</h2>
<h2>Event Polls</h2>
{% if object.polls.all %}
<ul class="list-group">
{% for poll in object.polls.all %}
<li class="list-group-item"><a href="{% url 'polls:view-poll' event_id=event.id poll_num=forloop.counter %}">{{ poll.question_text }}</a></li>
{% endfor %}
</ul>
{% for poll in object.polls.all %}
<hr/>
<h3>Poll: {{ poll.question_text }} (<a href="{% url 'polls:view-poll' event_id=event.id poll_num=forloop.counter %}">Edit</a>)</h3>
<br/>
<h4>Poll Options:</h4>
<ul class="list-group">
{% for option in poll.options.all %}
<li class="list-group-item">{{ option.choice_text }}</li>
{% endfor %}
</ul>
<p>Minimum Number of Option Selections: {{ poll.min_num_selections }}. Maximum Number of Option Selections: {{ poll.max_num_selections }}.</p>
{% endfor %}
{% else %}
<p>No polls are available for this Event.</p>
{% endif %}
{% if is_organiser %}
<hr/>
<a href="{% url 'polls:create-poll' event.id %}" class="btn btn-default" role="button">Add Poll</a>
{% endif %}
{% endblock %}

View File

@ -1,4 +1,5 @@
// Form submission and validation
var updateModes = {create: 1, update: 2, delete: 3};
var submitBtn = $("#submit-event-create");
var submitBtnLabel = "Create Event";
var submitBtnWaitLabel = "Please wait...";
@ -10,6 +11,8 @@ var generalErrorBlock = document.getElementById('all-errors-help-block');
var errors = [];
var create = true;
var pollCount = 0;
var pollIndex = 0;
var pollEditActive = false;
var numOfOpts = 2;
function finalisePolls() {
@ -278,12 +281,12 @@ function isPollQValid() {
var valid = true;
// Check question is valid
var question = $('#question-name-input-' + pollCount).val();
var question = $('#question-name-input-' + pollIndex).val();
if(question === '') {
checkAndAddError({
error: "Question / Statement for the poll is blank.",
helpBlockId: "question-input-error-block-" + pollCount
helpBlockId: "question-input-error-block-" + pollIndex
});
valid = false;
@ -294,8 +297,8 @@ function isPollQValid() {
function isPollOptionsValid() {
var valid = true;
var optsInputs = $('.option-formset #option-name-input-' + pollCount);
var helpBlockId = "options-input-error-block-" + pollCount;
var optsInputs = $('.option-formset #option-name-input-' + pollIndex);
var helpBlockId = "options-input-error-block-" + pollIndex;
if(numOfOpts < 1) {
checkAndAddError({
@ -337,10 +340,10 @@ function isPollOptionsValid() {
function isMinMaxSelectionValid() {
var valid = true;
var minInput = $('#minimum-input-' + pollCount);
var minInput = $('#minimum-input-' + pollIndex);
var minInputMinAttr = parseInt(minInput[0].min);
var minInputVal = minInput.val();
var helpBlockId = "selections-input-error-block-" + pollCount;
var helpBlockId = "selections-input-error-block-" + pollIndex;
var errorStr = "";
if(minInputVal === "" || minInputVal < minInputMinAttr) {
@ -351,7 +354,7 @@ function isMinMaxSelectionValid() {
valid = false;
}
var maxInput = $('#maximum-input-' + pollCount);
var maxInput = $('#maximum-input-' + pollIndex);
var maxInputMinAttr = parseInt(maxInput[0].min);
var maxInputVal = maxInput.val();
@ -365,7 +368,7 @@ function isMinMaxSelectionValid() {
valid = false;
} else if (maxInputVal > numOfOpts) {
if (errorStr !== '') {
errorStr = errorStr + " and the same applies to the maximum";
errorStr = errorStr + " and the maximum cannot be more than the number of options (" + numOfOpts + ")";
} else {
errorStr = "The maximum option selection value (" + maxInputVal + ") cannot be more than the number of options (" + numOfOpts + ")";
}
@ -803,14 +806,14 @@ function updateFormset(formset) { // Ported from DEMOS 1. Updates the row number
var forms = formset.children('.formset-form:not(.formset-form-empty, .formset-form-removed)');
var removedForms = formset.children('.formset-form.formset-form-removed');
forms.each(function(index) {
updateForm($(this), index);
updateForm($(this), index, updateModes.update);
});
removedForms.each(function(index) {
updateForm($(this), forms.length + index);
updateForm($(this), forms.length + index, updateModes.delete);
});
}
function updateForm(form, formIndex) { // Ported from DEMOS 1.
function updateForm(form, formIndex, mode) { // Ported from DEMOS 1.
// Specific update for option forms
var mayBeTextInput = form.find('input:text')[0];
if(mayBeTextInput !== undefined && mayBeTextInput.placeholder !== undefined) {
@ -825,6 +828,16 @@ function updateForm(form, formIndex) { // Ported from DEMOS 1.
var formset = form.parent('.formset');
var formsetPrefix = formset.attr('data-formset-prefix');
if (formsetPrefix === 'polls' && mode === updateModes.update) {
// Get a reference to the fields that need updating from the form including the table
var formFields = form.find('.formset-form-fields:first >');
var table = form.find('.table:first');
// Perform the ID updates on the fields based on the poll index
performFormInputUpdates(formFields, table, formIndex);
}
var formPrefix = formsetPrefix + '-' + formIndex;
var formPrefixRegex = new RegExp(formsetPrefix + '-(?:__prefix__|\\d+)');
form.find('*').addBack().each(function(index, element) {
@ -848,54 +861,61 @@ function manageTotalForms(formset, value) { // Ported from DEMOS1.
addButton.prop('disabled', parseInt(totalForms.val()) - removedForms.length >= parseInt(maxNumForms.val()));
}
function updateQuestionFormInputs(form) {
// Update the name and IDs of all the dialog input fields
function updatePollFormInputs(form) {
// Obtain the cloned input fields for the dialog in order to update them
var clonedFields = form.find('.formset-form-fields:first >');
// Update the table ID
// Obtain a reference to the options table
var table = form.find('.table:first');
table.attr("id", "options-table-" + pollCount);
// Perform the ID updates on the fields based on the poll index
performFormInputUpdates(clonedFields, table, pollIndex);
}
function performFormInputUpdates(fields, table, index) {
// Update the table ID
table.attr("id", "options-table-" + index);
// Update the poll question / statement ID
clonedFields.find(".dialogQ:first")
.attr("id", "question-name-input-" + pollCount)
.attr("name", "question-name-input-" + pollCount);
fields.find(".dialogQ:first")
.attr("id", "question-name-input-" + index)
.attr("name", "question-name-input-" + index);
// Update one of the help block IDs for various sections of the dialog
var pollQuestionErrorHelpBlock = clonedFields.find("#question-input-error-block");
pollQuestionErrorHelpBlock.attr("id", "question-input-error-block-" + pollCount);
var pollQuestionErrorHelpBlock = fields.find("#question-input-error-block");
pollQuestionErrorHelpBlock.attr("id", "question-input-error-block-" + index);
var pollOptionsErrorHelpBlock = clonedFields.find("#options-input-error-block");
pollOptionsErrorHelpBlock.attr("id", "options-input-error-block-" + pollCount);
var pollOptionsErrorHelpBlock = fields.find("#options-input-error-block");
pollOptionsErrorHelpBlock.attr("id", "options-input-error-block-" + index);
var pollSelectionsErrorHelpBlock = clonedFields.find("#selections-input-error-block");
pollSelectionsErrorHelpBlock.attr("id", "selections-input-error-block-" + pollCount);
var pollSelectionsErrorHelpBlock = fields.find("#selections-input-error-block");
pollSelectionsErrorHelpBlock.attr("id", "selections-input-error-block-" + index);
// Update the poll option input IDs
var optsInputs = clonedFields.find(".dialogO");
var optsInputs = fields.find(".dialogO");
for(var i = 0; i < optsInputs.length; i++) {
var input = optsInputs[i];
input.id = "option-name-input-" + pollCount;
input.name = "option-name-input-" + pollCount;
input.id = "option-name-input-" + index;
input.name = "option-name-input-" + index;
}
// Update the data-formset-prefix for correct referencing
var dataFormsetPrefix = "options-" + pollCount;
var optionFormSet = clonedFields.find(".option-formset");
var dataFormsetPrefix = "options-" + index;
var optionFormSet = fields.find(".option-formset");
optionFormSet.attr("data-formset-prefix", dataFormsetPrefix);
var addPollOptBtn = clonedFields.find('.formset-add');
var addPollOptBtn = fields.find('.formset-add');
addPollOptBtn.attr("data-formset-prefix", dataFormsetPrefix);
// Update the poll min and max selection
clonedFields.find(".min-input:first")
.attr("id", "minimum-input-" + pollCount)
.attr("name", "minimum-input-" + pollCount);
fields.find(".min-input:first")
.attr("id", "minimum-input-" + index)
.attr("name", "minimum-input-" + index);
clonedFields.find(".max-input:first")
.attr("id", "maximum-input-" + pollCount)
.attr("name", "maximum-input-" + pollCount);
fields.find(".max-input:first")
.attr("id", "maximum-input-" + index)
.attr("name", "maximum-input-" + index);
}
function isDialogFormValid() {
@ -904,7 +924,7 @@ function isDialogFormValid() {
var minMaxSelValid = true;
// Check question is valid
var pollQErrorHelpBlockId = "question-input-error-block-" + pollCount;
var pollQErrorHelpBlockId = "question-input-error-block-" + pollIndex;
pollQValid = isPollQValid();
if(pollQValid === true) {
@ -914,7 +934,7 @@ function isDialogFormValid() {
}
// Check opts are valid
var pollOptsErrorHelpBlockId = "options-input-error-block-" + pollCount;
var pollOptsErrorHelpBlockId = "options-input-error-block-" + pollIndex;
optsValid = isPollOptionsValid();
if(optsValid === true) {
@ -924,7 +944,7 @@ function isDialogFormValid() {
}
// Check min and max selections are valid
var pollSelErrorHelpBlockId = "selections-input-error-block-" + pollCount;
var pollSelErrorHelpBlockId = "selections-input-error-block-" + pollIndex;
minMaxSelValid = isMinMaxSelectionValid();
if(minMaxSelValid === true) {
@ -937,8 +957,8 @@ function isDialogFormValid() {
}
function updateSelectionsMaxAtrr() {
var minInput = $('#minimum-input-' + pollCount);
var maxInput = $('#maximum-input-' + pollCount);
var minInput = $('#minimum-input-' + pollIndex);
var maxInput = $('#maximum-input-' + pollIndex);
// Get the vals from the selection inputs and update them if they exceed the new max
var minInputVal = minInput.val();
@ -967,23 +987,29 @@ $('.formset-add').click(function (e) { // Ported from DEMOS1
switch (formsetPrefix) {
case "polls":
// Update the IDs and names of all of the cloned input form fields based on the number of polls
updateQuestionFormInputs(form);
// Set the index
pollIndex = pollCount;
// 2 is the default number shown upon the launch of the dialog
// Update the IDs and names of all of the cloned input form fields based on the number of polls
updatePollFormInputs(form);
// 2 is the default number of opts shown upon the launch of the dialog
numOfOpts = 2;
// New poll is being created so edit mode hasn't been activated
pollEditActive = false;
break;
case "options-" + pollCount:
case "options-" + pollIndex:
numOfOpts++;
updateSelectionsMaxAtrr();
clearError("options-input-error-block-" + pollCount);
clearError("options-input-error-block-" + pollIndex);
break;
}
var formIndex = formset.children('.formset-form:not(.formset-form-empty)').length;
formset.append(form);
updateForm(form, formIndex);
updateForm(form, formIndex, updateModes.create);
emptyFormCheckedInputs.each(function (index) {
$(this).prop('checked', true);
});
@ -1010,19 +1036,23 @@ $('.formset-form-remove').click(function (e) { // Ported from DEMOS1
form.remove();
manageTotalForms(formset, -1);
}
// We need to reduce the poll count if we've removed a poll
if(formPrefix === "poll") {
pollCount--;
}
// Update the formset and inform that a form has been removed
updateFormset(formset);
formset.trigger('formsetFormRemoved');
// Perform validation now that a row has been removed
// Perform validation and other operations now that a row has been removed based on the affected table
switch (formPrefix) {
case 'poll':
// TODO: A poll has been removed so we need to update the poll count
break;
case 'option':
// Decrement the number of total options and validate the options list
numOfOpts--;
updateSelectionsMaxAtrr();
validateFormField(isPollOptionsValid, "options-input-error-block-" + pollCount);
validateFormField(isPollOptionsValid, "options-input-error-block-" + pollIndex);
break;
case 'organiser':
validateFormField(areOrganisersEmailsValid, "organisers-input-error-block");
@ -1037,25 +1067,35 @@ $('.formset-form-save').click(function (e) {
var dialogValid = isDialogFormValid();
if(dialogValid === true) {
// TODO: Clear errors
var modal = $(this).closest('.modal');
var form = modal.data('form');
var name = $('#question-name-input-' + pollCount).val();
var name = $('#question-name-input-' + pollIndex).val();
form.find('.formset-form-name:first').text(name);
modal.data('formSave', true);
modal.modal('hide');
// Increment the poll count and clear any validation errors
pollCount++;
if(!pollEditActive) {
// Increment the poll count and clear any validation errors
pollCount++;
} else {
pollEditActive = false;
}
clearError("polls-input-error-block");
} else {
//highlightErrors();
}
});
function extractPollIndexFromId(id) {
var idSplitArray = id.split('-');
pollIndex = parseInt(idSplitArray[3]);
}
$('.formset-form-edit').click(function (e) {
var form = $(this).closest('.formset-form');
var questionNameInput = form.find('.formset-form-fields:first > .dialogFormField > .dialogQ');
extractPollIndexFromId(questionNameInput.attr('id'));
$('#formset-modal').data('form', form).modal('show');
pollEditActive = true;
});
$('#formset-modal').on('show.bs.modal', function (e) { // Ported from DEMOS1
@ -1074,7 +1114,7 @@ $('#formset-modal').on('show.bs.modal', function (e) { // Ported from DEMOS1
formset.trigger('formsetModalShow', [modalBody]);
// Attach an event handler for poll option row sorting
$("#options-table-" + pollCount).sortable({
$("#options-table-" + pollIndex).sortable({
items: "tr",
update: update
});