refactor async form js util

This commit is contained in:
Felix Hamann 2019-04-09 21:55:58 +02:00
parent 63aef9e362
commit 559fb3fb9d
5 changed files with 69 additions and 36 deletions

View File

@ -1071,6 +1071,7 @@ siteLayout' headingOverride widget = do
-- addScript $ StaticR js_utils_alerts_js
-- addScript $ StaticR js_utils_asidenav_js
-- JavaScript utils
addScript $ StaticR js_utils_asyncForm_js
addScript $ StaticR js_utils_asyncTable_js
addScript $ StaticR js_utils_checkAll_js
addScript $ StaticR js_utils_form_js

View File

@ -121,7 +121,7 @@ postAdminTestR = do
let emailWidget' = wrapForm emailWidget def
{ formAction = Just . SomeRoute $ AdminTestR
, formEncoding = emailEnctype
, formAttrs = [("data-ajax-submit", "")]
, formAttrs = [("uw-async-form", "")]
}

View File

@ -261,7 +261,7 @@ postHelpR = do
let form = wrapForm formWidget def
{ formAction = Just $ SomeRoute HelpR
, formEncoding = formEnctype
, formAttrs = [ ("data-ajax-submit", "") | isModal ]
, formAttrs = [ ("uw-async-form", "") | isModal ]
}
formResultModal res HelpR $ \HelpForm{..} -> do

View File

@ -1,4 +1,4 @@
.async-form-response {
.async-form__response {
margin: 20px 0;
position: relative;
width: 100%;
@ -7,15 +7,15 @@
padding-top: 60px;
}
.async-form-response::before,
.async-form-response::after {
.async-form__response::before,
.async-form__response::after {
position: absolute;
top: 0px;
left: 50%;
display: block;
}
.async-form-response--success::before {
.async-form__response--success::before {
content: '';
width: 17px;
height: 28px;
@ -24,7 +24,7 @@
transform: translateX(-50%) rotate(45deg);
}
.async-form-response--info::before {
.async-form__response--info::before {
content: '';
width: 5px;
height: 30px;
@ -32,7 +32,7 @@
background-color: #777;
transform: translateX(-50%);
}
.async-form-response--info::after {
.async-form__response--info::after {
content: '';
width: 5px;
height: 5px;
@ -40,14 +40,14 @@
transform: translateX(-50%);
}
.async-form-response--warning::before {
.async-form__response--warning::before {
content: '';
width: 5px;
height: 30px;
background-color: rgb(255, 187, 0);
transform: translateX(-50%);
}
.async-form-response--warning::after {
.async-form__response--warning::after {
content: '';
width: 5px;
height: 5px;
@ -56,14 +56,14 @@
transform: translateX(-50%);
}
.async-form-response--error::before {
.async-form__response--error::before {
content: '';
width: 5px;
height: 40px;
background-color: #940d0d;
transform: translateX(-50%) rotate(-45deg);
}
.async-form-response--error::after {
.async-form__response--error::after {
content: '';
width: 5px;
height: 40px;
@ -71,7 +71,7 @@
transform: translateX(-50%) rotate(45deg);
}
.async-form-loading {
.async-form--loading {
opacity: 0.1;
transition: opacity 800ms ease-out;
pointer-events: none;

View File

@ -1,33 +1,62 @@
(function collonadeClosure() {
'use strict';
window.utils = window.utils || {};
/**
*
* Async Form Utility
* prevents form submissions from reloading the page but instead firing an AJAX request
*
* Attribute: uw-async-form
* (works only on <form> elements)
*
* Example usage:
* <form uw-async-form method='POST' action='...'>
* ...
*/
var ASYNC_FORM_RESPONSE_CLASS = 'async-form-response';
var ASYNC_FORM_LOADING_CLASS = 'async-form-loading';
var ASYNC_FORM_UTIL_NAME = 'asyncForm';
var ASYNC_FORM_UTIL_SELECTOR = 'form[uw-async-form]';
var ASYNC_FORM_INITIALIZED_CLASS = 'check-all--initialized';
var ASYNC_FORM_RESPONSE_CLASS = 'async-form__response';
var ASYNC_FORM_LOADING_CLASS = 'async-form--loading';
var ASYNC_FORM_MIN_DELAY = 600;
var DEFAULT_FAILURE_MESSAGE = 'The response we received from the server did not match what we expected. Please let us know this happened via the help widget in the top navigation.';
var ASYNC_FORM_DEFAULT_FAILURE_MESSAGE = 'The response we received from the server did not match what we expected. Please let us know this happened via the help widget in the top navigation.';
window.utils.asyncForm = function(formElement, options) {
options = options || {};
var asyncFormUtil = function(element) {
var lastRequestTimestamp = 0;
function setup() {
formElement.addEventListener('submit', submitHandler);
function init() {
if (!element) {
throw new Error('Async Form Utility cannot be setup without an element!');
}
if (element.classList.contains(ASYNC_FORM_INITIALIZED_CLASS)) {
return false;
}
element.addEventListener('submit', submitHandler);
element.classList.add(ASYNC_FORM_INITIALIZED_CLASS);
return {
name: ASYNC_FORM_UTIL_NAME,
element: element,
destroy: function() {},
};
}
function processResponse(response) {
var responseElement = makeResponseElement(response.content, response.status);
var parentElement = formElement.parentElement;
var parentElement = element.parentElement;
// make sure there is a delay between click and response
var delay = Math.max(0, ASYNC_FORM_MIN_DELAY + lastRequestTimestamp - Date.now());
setTimeout(function() {
parentElement.insertBefore(responseElement, formElement);
formElement.remove();
parentElement.insertBefore(responseElement, element);
element.remove();
}, delay);
}
@ -43,12 +72,12 @@
function submitHandler(event) {
event.preventDefault();
formElement.classList.add(ASYNC_FORM_LOADING_CLASS)
element.classList.add(ASYNC_FORM_LOADING_CLASS)
lastRequestTimestamp = Date.now();
var url = formElement.getAttribute('action');
var url = element.getAttribute('action');
var headers = { };
var body = new FormData(formElement);
var body = new FormData(element);
if (options && options.headers) {
Object.keys(options.headers).forEach(function(headerKey) {
@ -66,21 +95,24 @@
}).then(function(response) {
processResponse(response[0]);
}).catch(function(error) {
var failureMessage = DEFAULT_FAILURE_MESSAGE;
var failureMessage = ASYNC_FORM_DEFAULT_FAILURE_MESSAGE;
if (options.i18n && options.i18n.asyncFormFailure) {
failureMessage = options.i18n.asyncFormFailure;
}
processResponse({ content: failureMessage });
formElement.classList.remove(ASYNC_FORM_LOADING_CLASS);
element.classList.remove(ASYNC_FORM_LOADING_CLASS);
});
}
setup();
return {
scope: formElement,
destroy: function() {},
};
return init();
};
if (UtilRegistry) {
UtilRegistry.register({
name: ASYNC_FORM_UTIL_NAME,
selector: ASYNC_FORM_UTIL_SELECTOR,
setup: asyncFormUtil
});
}
})();