Skip to content

Commit

Permalink
Improve front-end validation
Browse files Browse the repository at this point in the history
  • Loading branch information
maurofmferrao committed Nov 8, 2024
1 parent d9226db commit a5c937e
Show file tree
Hide file tree
Showing 10 changed files with 173 additions and 168 deletions.
64 changes: 26 additions & 38 deletions ui/src/campaign-builder/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -283,44 +283,32 @@ window.cB = {
// Load a map
cB.initialiseMap('campaign-builder-map', $dialog);

$dialog.find('.XiboForm').validate({
submitHandler: function(form) {
XiboFormSubmit($(form), null, () => {
// Is this an add or an edit?
const displayOrder = $form.data('existingDisplayOrder');
if (displayOrder && parseInt(displayOrder) > 0) {
// Delete the existing assignment
$.ajax({
method: 'delete',
url: $form.data('assignmentRemoveUrl') +
'&displayOrder=' + displayOrder,
complete: () => {
refreshLayoutAssignmentsTable();
},
});
} else {
refreshLayoutAssignmentsTable();
}
});
},
errorElement: 'span',
highlight: function(element) {
$(element).closest('.form-group')
.removeClass('has-success')
.addClass('has-error');
},
success: function(element) {
$(element).closest('.form-group')
.removeClass('has-error')
.addClass('has-success');
},
invalidHandler: function(event, validator) {
// Remove the spinner
$(this).closest('.modal-dialog').find('.saving').remove();
$(this).closest('.modal-dialog').find('.save-button')
.removeClass('disabled');
// Validate form
forms.validateForm(
$dialog.find('.XiboForm'), // form
$dialog, // container
{
submitHandler: function(form) {
XiboFormSubmit($(form), null, () => {
// Is this an add or an edit?
const displayOrder = $form.data('existingDisplayOrder');
if (displayOrder && parseInt(displayOrder) > 0) {
// Delete the existing assignment
$.ajax({
method: 'delete',
url: $form.data('assignmentRemoveUrl') +
'&displayOrder=' + displayOrder,
complete: () => {
refreshLayoutAssignmentsTable();
},
});
} else {
refreshLayoutAssignmentsTable();
}
});
},
},
});
);
}).on('hidden.bs.modal', function() {
// Clear the layout select
if (cB.$layoutSelect) {
Expand Down Expand Up @@ -409,7 +397,7 @@ window.cB = {
{
id: 'assignment_button_delete',
text: campaignBuilderTrans.assignmentDeleteButton,
url: $selector.data('assignmentDeleteUrl')+
url: $selector.data('assignmentDeleteUrl') +
'?displayOrder=' + data.displayOrder,
},
];
Expand Down
64 changes: 64 additions & 0 deletions ui/src/core/forms.js
Original file line number Diff line number Diff line change
Expand Up @@ -5009,4 +5009,68 @@ window.forms = {
}
});
},

validateForm: function(
form,
container,
options,
) {
const defaultOptions = {
submitHandler: options.submitHandler,
errorElement: 'span',
// Ignore the date picker helpers
ignore: '.datePickerHelper',
errorPlacement: function(error, element) {
if ($(element).hasClass('dateControl')) {
// Places the error label date controller
error.insertAfter(element.parent());
} else {
// Places the error label after the invalid element
error.insertAfter(element);
}
},
highlight: function(element) {
$(element).closest('.form-group')
.removeClass('has-success')
.addClass('has-error');
},
success: function(element) {
$(element).closest('.form-group')
.removeClass('has-error')
.addClass('has-success');
},
invalidHandler: function(event, validator) {
// Mark non active tabs with error if they have an invalid element
$('.nav-item a.nav-link').removeClass('has-error');
validator.errorList.forEach((error) => {
const element = error.element;
$(element).parents('.tab-pane').each((_id, tab) => {
$('.nav-item a.nav-link[href="#' +
$(tab).attr('id') + '"]:not(.active)')
.addClass('has-error');
});
});

// Remove the spinner
$(this).closest('.modal-dialog').find('.saving').remove();
// https://github.com/xibosignage/xibo/issues/1589
$(this).closest('.modal-dialog').find('.save-button')
.removeClass('disabled');
},
};

// Merge options with defaults
Object.assign(
defaultOptions,
options,
);

// Init validator
const validatorObj = form.validate(defaultOptions);

// If we are in a modal, validate on tab change
container.find('.nav-link').on('shown.bs.tab', () => {
validatorObj.form();
});
},
};
65 changes: 21 additions & 44 deletions ui/src/core/xibo-cms.js
Original file line number Diff line number Diff line change
Expand Up @@ -139,38 +139,16 @@ window.XiboInitialise = function(scope, options) {
return false;
});

// Search for any forms that will need submitting
// NOTE: The validation plugin does not like binding
// to multiple forms at once.
$(scope + ' .XiboForm').validate({
submitHandler: XiboFormSubmit,
// Ignore the date picker helpers
ignore: '.datePickerHelper, :hidden>*:not(.flatpickr-input)',
errorElement: 'span',
errorPlacement: function(error, element) {
if ($(element).hasClass('dateControl')) {
// Places the error label date controller
error.insertAfter(element.parent());
} else {
// Places the error label after the invalid element
error.insertAfter(element);
}
},
highlight: function(element) {
$(element).closest('.form-group').removeClass('has-success')
.addClass('has-error');
},
success: function(element) {
$(element).closest('.form-group').removeClass('has-error')
.addClass('has-success');
},
invalidHandler: function(event, validator) {
// Remove the spinner
$(this).closest('.modal-dialog').find('.saving').remove();
// https://github.com/xibosignage/xibo/issues/1589
$(this).closest('.modal-dialog').find('.save-button')
.removeClass('disabled');
},
// Form validation
$(scope + ' .XiboForm').each((_idx, form) => {
const $form = $(form);
forms.validateForm(
$form, // form
$form.parent(), // container
{
submitHandler: XiboFormSubmit,
},
);
});

// Links that just need to be submitted as forms
Expand All @@ -193,18 +171,17 @@ window.XiboInitialise = function(scope, options) {
return false;
});

// Search for any text forms that will need submitting
$(scope + ' .XiboTextForm').validate({
submitHandler: XiboFormSubmit,
errorElement: 'span',
highlight: function(element) {
$(element).closest('.form-group').removeClass('has-success')
.addClass('has-error');
},
success: function(element) {
$(element).closest('.form-group').removeClass('has-error')
.addClass('has-success');
},
// Text Form validation
$(scope + ' .XiboTextForm').each((_idx, form) => {
const $form = $(form);
console.log('validateForm XiboTextForm');
forms.validateForm(
$form, // form
$form.parent(), // container
{
submitHandler: XiboFormSubmit,
},
);
});

// Search for any help enabled elements
Expand Down
2 changes: 1 addition & 1 deletion views/layout-form-edit.twig
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@

{% set title %}{% trans "Name" %}{% endset %}
{% set helpText %}{% trans "The Name of the Layout - (1 - 50 characters)" %}{% endset %}
{{ forms.input("name", title, layout.layout, helpText) }}
{{ forms.input("name", title, layout.layout, helpText, "", "required") }}

{% if currentUser.featureEnabled("tag.tagging") %}
{% set title %}{% trans "Tags" %}{% endset %}
Expand Down
2 changes: 1 addition & 1 deletion views/notification-form-add.twig
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
{% set title %}{% trans "Add the body of your message in the box below. If you are going to target this message to a Display/DisplayGroup be aware that the formatting you apply here will be removed." %}{% endset %}
{{ forms.message(title) }}

{{ forms.textarea("body", "", "", "", "", 10) }}
{{ forms.textarea("body", "", "", "", "", "required", 10) }}
</div>
<div class="tab-pane" id="audience">
{% set title %}{% trans "Users" %}{% endset %}
Expand Down
4 changes: 2 additions & 2 deletions views/notification-form-edit.twig
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
<li class="nav-item"><a class="nav-link" href="#audience" role="tab" data-toggle="tab"><span>{% trans "Audience" %}</span></a></li>
<li class="nav-item"><a class="nav-link" href="#attachment" role="tab" data-toggle="tab"><span>{% trans "Attachment" %}</span></a></li>
</ul>
<form id="notificationForm" class="XiboForm form-horizontal" method="put" action="{{ url_for("notification.edit", {id: notification.notificationId}) }}">
<form id="notificationForm" class="form-horizontal" method="put" action="{{ url_for("notification.edit", {id: notification.notificationId}) }}">
<div class="tab-content">
<div class="tab-pane active" id="general">
{% set title %}{% trans "Subject" %}{% endset %}
Expand All @@ -64,7 +64,7 @@
{% set title %}{% trans "Add the body of your message in the box below. If you are going to target this message to a Display/DisplayGroup be aware that the formatting you apply here will be removed." %}{% endset %}
{{ forms.message(title) }}

{{ forms.textarea("body", "", notification.body, "", "", 10) }}
{{ forms.textarea("body", "", notification.body, "", "", "required", 10) }}
</div>
<div class="tab-pane" id="audience">
{% set title %}{% trans "Users" %}{% endset %}
Expand Down
45 changes: 16 additions & 29 deletions views/notification-page.twig
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,15 @@
var attachmentFormSetup = function(dialog) {
// Conjure up a text editor
formHelpers.createCKEditor('notificationMessage', $('#body'));
formHelpers.createCKEditor('notificationMessage', $('#body')).then((editor) => {
// Update text area when unfocusing editor
editor.ui.focusTracker.on('change:isFocused', (evt, name, isFocused ) => {
if (!isFocused) {
// Update editor
formHelpers.updateCKEditor('notificationMessage');
}
});
});
// Make sure when we close the dialog we also destroy the editor
dialog.on("hide.bs.modal", function(event) {
Expand All @@ -207,35 +215,14 @@
notificationAddFormAttachmentButtonClicked(e, dialog);
});
// Search for any forms that will need submitting
// NOTE: The validation plugin does not like binding to multiple forms at once.
dialog.find("#notificationForm").validate({
submitHandler: attachmentFormSubmit,
errorElement: "span",
// Ignore the date picker helpers
ignore: '.datePickerHelper, :hidden>*:not(.flatpickr-input)',
errorPlacement: function(error, element) {
if($(element).hasClass('dateControl')) {
// Places the error label date controller
error.insertAfter(element.parent());
} else {
// Places the error label after the invalid element
error.insertAfter(element);
}
},
highlight: function(element) {
$(element).closest('.form-group').removeClass('has-success').addClass('has-error');
// Validate form
forms.validateForm(
dialog.find('#notificationForm'), // form
dialog, // container
{
submitHandler: attachmentFormSubmit,
},
success: function(element) {
$(element).closest('.form-group').removeClass('has-error').addClass('has-success');
},
invalidHandler: function(event, validator) {
// Remove the spinner
$(this).closest(".modal-dialog").find(".saving").remove();
// https://github.com/xibosignage/xibo/issues/1589
$(this).closest(".modal-dialog").find(".save-button").removeClass("disabled");
}
});
);
};
/**
Expand Down
52 changes: 22 additions & 30 deletions views/user-page.twig
Original file line number Diff line number Diff line change
Expand Up @@ -242,39 +242,31 @@
initFolderPanel(dialog, true);
// Bind to the add form submit
$(".UserForm").validate({
submitHandler: function (form) {
// Grab and alter the value in the library quota field
var libraryQuotaField = $(form).find("input[name=libraryQuota]");
var libraryQuotaUnitsField = $(form).find("select[name=libraryQuotaUnits]");
var libraryQuota = libraryQuotaField.val();
if (libraryQuotaUnitsField.val() === 'mb') {
libraryQuota = libraryQuota * 1024;
} else if (libraryQuotaUnitsField.val() === 'gb') {
libraryQuota = libraryQuota * 1024 * 1024;
}
// Validate form
var $userForm = $('.UserForm');
forms.validateForm(
$userForm, // form
$userForm.parents('.modal-body'), // container
{
submitHandler: function (form) {
// Grab and alter the value in the library quota field
var libraryQuotaField = $(form).find('input[name=libraryQuota]');
var libraryQuotaUnitsField = $(form).find('select[name=libraryQuotaUnits]');
var libraryQuota = libraryQuotaField.val();
if (libraryQuotaUnitsField.val() === 'mb') {
libraryQuota = libraryQuota * 1024;
} else if (libraryQuotaUnitsField.val() === 'gb') {
libraryQuota = libraryQuota * 1024 * 1024;
}
// Set the field
libraryQuotaField.prop('value', libraryQuota);
// Set the field
libraryQuotaField.prop('value', libraryQuota);
XiboFormSubmit(form);
},
errorElement: "span",
highlight: function(element) {
$(element).closest('.form-group').removeClass('has-success').addClass('has-error');
},
success: function(element) {
$(element).closest('.form-group').removeClass('has-error').addClass('has-success');
XiboFormSubmit(form);
},
},
invalidHandler: function(event, validator) {
// Remove the spinner
$(this).closest(".modal-dialog").find(".saving").remove();
// https://github.com/xibosignage/xibo/issues/1589
$(this).closest(".modal-dialog").find(".save-button").removeClass("disabled");
}
});
);
}
/**
Expand Down
Loading

0 comments on commit a5c937e

Please sign in to comment.