Skip to content

Commit

Permalink
Merge pull request #1792 from creative-commoners/pulls/2/always-inlin…
Browse files Browse the repository at this point in the history
…e-submit-elements

NEW Wait for promises before submitting forms
  • Loading branch information
GuySartorelli authored Jul 24, 2024
2 parents 2bcb6ec + ef32740 commit 7e3f3a5
Show file tree
Hide file tree
Showing 4 changed files with 112 additions and 62 deletions.
2 changes: 1 addition & 1 deletion client/dist/js/bundle.js

Large diffs are not rendered by default.

8 changes: 8 additions & 0 deletions client/src/boot/applyTransforms.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,14 @@ const applyTransforms = () => {
updater.form.addValidation(
'*',
(values, Validation, schema) => {
// Hardcoded exclusion for elemental inline forms
// This is done so that client-side validation doesn't run so that all validation is done
// on the server. This is done so that invalid, closed elements will popup open and
// also trigger a toast notification.
// Note that this has no effect on non-inline forms as they do not use form schema
if (schema.name.indexOf('ElementForm_') === 0) {
return Validation.getState();
}
const validator = new Validator(values);
const errorMap = Object.keys(values).reduce((curr, key) => {
const field = findField(schema.fields, key);
Expand Down
2 changes: 1 addition & 1 deletion client/src/components/HtmlEditorField/HtmlEditorField.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ class HtmlEditorField extends TextField {
*/
registerChangeListener() {
const target = this.getEditorElement();
this.getEditor().on('change keyup setcontent', () => {
this.getEditor().on('change keyup', () => {
super.handleChange({ target });
});
}
Expand Down
162 changes: 102 additions & 60 deletions client/src/legacy/LeftAndMain.js
Original file line number Diff line number Diff line change
Expand Up @@ -460,77 +460,119 @@ $.entwine('ss', function($) {
// default to first button if none given - simulates browser behaviour
if(!button) button = this.find('.btn-toolbar :submit:first');

form.trigger('beforesubmitform');
this.trigger('submitform', {form: form, button: button});
var beforeSubmitFormEventData = {
// array of promises that must resolve({success:true}) before the form is submitted
// result of each promise must be an object of
// { success: <bool>, reason: <string> } where reason should be populated if success is false
promises: [],
// callbacks that are called on ajax success after submitted the form
onAjaxSuccessCallbacks: [],
};
form.trigger('beforesubmitform', beforeSubmitFormEventData);

Promise.all(beforeSubmitFormEventData.promises).then(function(results) {

let success = true;
const reasons = [];
for (const result of results) {
if (result['success'] === false) {
success = false;
reasons.push(result['reason']);
}
}
if (!success) {
let invalid = false;
for (const reason of reasons) {
if (reason === 'invalid') {
invalid = true;
break;
}
}
if (invalid) {
jQuery.noticeAdd({
text: window.ss.i18n._t('Admin.VALIDATIONERROR', 'Validation Error'),
type: 'error',
stayTime: 5000,
inEffect: {
left: '0',
opacity: 'show'
}
});
}
return false;
}

// set button to "submitting" state
$(button).addClass('btn--loading loading');
$(button).prop('disabled', true);
self.trigger('submitform', {form: form, button: button});

if($(button).is('button')) {
$(button).data('original-text', $(button).text());
// set button to "submitting" state
$(button).addClass('btn--loading loading');
$(button).prop('disabled', true);

$(button).append($(
'<div class="btn__loading-icon">'+
'<span class="btn__circle btn__circle--1"></span>'+
'<span class="btn__circle btn__circle--2"></span>'+
'<span class="btn__circle btn__circle--3"></span>'+
'</div>'));
if($(button).is('button')) {

$(button).css($(button).outerWidth() + 'px');
}
$(button).append($(
'<div class="btn__loading-icon">'+
'<span class="btn__circle btn__circle--1"></span>'+
'<span class="btn__circle btn__circle--2"></span>'+
'<span class="btn__circle btn__circle--3"></span>'+
'</div>'));

// validate if required
var validationResult = form.validate();
$(button).css($(button).outerWidth() + 'px');
}

var clearButton = function() {
$(button).removeClass('btn--loading loading');
$(button).prop('disabled', false);
$(button).find('.btn__loading-icon').remove();
$(button).css('width', 'auto');
$(button).text($(button).data('original-text'));
}
// validate if required
var validationResult = form.validate();

if(typeof validationResult!=='undefined' && !validationResult) {
statusMessage("Validation failed.", "bad");
clearButton();
}
var clearButton = function() {
$(button).removeClass('btn--loading loading');
$(button).prop('disabled', false);
$(button).find('.btn__loading-icon').remove();
$(button).css('width', 'auto');
$(button).text($(button).data('original-text'));
}

// get all data from the form
var formData = form.serializeArray();
// add button action
formData.push({name: $(button).attr('name'), value:'1'});
// Artificial HTTP referer, IE doesn't submit them via ajax.
// Also rewrites anchors to their page counterparts, which is important
// as automatic browser ajax response redirects seem to discard the hash/fragment.
formData.push({ name: 'BackURL', value: document.URL.replace(/\/$/, '') });

// Save tab selections so we can restore them later
this.saveTabState(window.ss.tabStateUrl(), false);

// Standard Pjax behaviour is to replace the submitted form with new content.
// The returned view isn't always decided upon when the request
// is fired, so the server might decide to change it based on its own logic,
// sending back different `X-Pjax` headers and content
jQuery.ajax(jQuery.extend({
headers: {"X-Pjax" : "CurrentForm,Breadcrumbs,ValidationResult"},
url: form.attr('action'),
data: formData,
type: 'POST',
complete: function() {
clearButton()
},
success: function(data, status, xhr) {
if(typeof validationResult!=='undefined' && !validationResult) {
statusMessage("Validation failed.", "bad");
clearButton();
form.removeClass('changed');
if(callback) callback(data, status, xhr);
}

var newContentEls = self.handleAjaxResponse(data, status, xhr);
if(!newContentEls) return;
// get all data from the form
var formData = form.serializeArray();
// add button action
formData.push({name: $(button).attr('name'), value:'1'});
// Artificial HTTP referer, IE doesn't submit them via ajax.
// Also rewrites anchors to their page counterparts, which is important
// as automatic browser ajax response redirects seem to discard the hash/fragment.
formData.push({ name: 'BackURL', value: document.URL.replace(/\/$/, '') });

// Save tab selections so we can restore them later
self.saveTabState(window.ss.tabStateUrl(), false);

// Standard Pjax behaviour is to replace the submitted form with new content.
// The returned view isn't always decided upon when the request
// is fired, so the server might decide to change it based on its own logic,
// sending back different `X-Pjax` headers and content
jQuery.ajax(jQuery.extend({
headers: {"X-Pjax" : "CurrentForm,Breadcrumbs,ValidationResult"},
url: form.attr('action'),
data: formData,
type: 'POST',
complete: function() {
clearButton()
},
success: function(data, status, xhr) {
beforeSubmitFormEventData.onAjaxSuccessCallbacks.forEach(fn => fn());
clearButton();
form.removeClass('changed');
if(callback) callback(data, status, xhr);

newContentEls.filter('form').trigger('aftersubmitform', {status: status, xhr: xhr, formData: formData});
}
}, ajaxOptions));
var newContentEls = self.handleAjaxResponse(data, status, xhr);
if(!newContentEls) return;

newContentEls.filter('form').trigger('aftersubmitform', {status: status, xhr: xhr, formData: formData});
}
}, ajaxOptions));
});

return false;
},
Expand Down

0 comments on commit 7e3f3a5

Please sign in to comment.