Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NEW Wait for promises before submitting forms #1792

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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) {
GuySartorelli marked this conversation as resolved.
Show resolved Hide resolved
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', () => {
GuySartorelli marked this conversation as resolved.
Show resolved Hide resolved
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) {
GuySartorelli marked this conversation as resolved.
Show resolved Hide resolved

let success = true;
const reasons = [];
for (const result of results) {
GuySartorelli marked this conversation as resolved.
Show resolved Hide resolved
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());
GuySartorelli marked this conversation as resolved.
Show resolved Hide resolved
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
Loading