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_alerts_js
-- addScript $ StaticR js_utils_asidenav_js -- addScript $ StaticR js_utils_asidenav_js
-- JavaScript utils -- JavaScript utils
addScript $ StaticR js_utils_asyncForm_js
addScript $ StaticR js_utils_asyncTable_js addScript $ StaticR js_utils_asyncTable_js
addScript $ StaticR js_utils_checkAll_js addScript $ StaticR js_utils_checkAll_js
addScript $ StaticR js_utils_form_js addScript $ StaticR js_utils_form_js

View File

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

View File

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

View File

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

View File

@ -1,33 +1,62 @@
(function collonadeClosure() { (function collonadeClosure() {
'use strict'; '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_UTIL_NAME = 'asyncForm';
var ASYNC_FORM_LOADING_CLASS = 'async-form-loading'; 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 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) { var asyncFormUtil = function(element) {
options = options || {};
var lastRequestTimestamp = 0; var lastRequestTimestamp = 0;
function setup() { function init() {
formElement.addEventListener('submit', submitHandler); 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) { function processResponse(response) {
var responseElement = makeResponseElement(response.content, response.status); 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 // make sure there is a delay between click and response
var delay = Math.max(0, ASYNC_FORM_MIN_DELAY + lastRequestTimestamp - Date.now()); var delay = Math.max(0, ASYNC_FORM_MIN_DELAY + lastRequestTimestamp - Date.now());
setTimeout(function() { setTimeout(function() {
parentElement.insertBefore(responseElement, formElement); parentElement.insertBefore(responseElement, element);
formElement.remove(); element.remove();
}, delay); }, delay);
} }
@ -43,12 +72,12 @@
function submitHandler(event) { function submitHandler(event) {
event.preventDefault(); event.preventDefault();
formElement.classList.add(ASYNC_FORM_LOADING_CLASS) element.classList.add(ASYNC_FORM_LOADING_CLASS)
lastRequestTimestamp = Date.now(); lastRequestTimestamp = Date.now();
var url = formElement.getAttribute('action'); var url = element.getAttribute('action');
var headers = { }; var headers = { };
var body = new FormData(formElement); var body = new FormData(element);
if (options && options.headers) { if (options && options.headers) {
Object.keys(options.headers).forEach(function(headerKey) { Object.keys(options.headers).forEach(function(headerKey) {
@ -66,21 +95,24 @@
}).then(function(response) { }).then(function(response) {
processResponse(response[0]); processResponse(response[0]);
}).catch(function(error) { }).catch(function(error) {
var failureMessage = DEFAULT_FAILURE_MESSAGE; var failureMessage = ASYNC_FORM_DEFAULT_FAILURE_MESSAGE;
if (options.i18n && options.i18n.asyncFormFailure) { if (options.i18n && options.i18n.asyncFormFailure) {
failureMessage = options.i18n.asyncFormFailure; failureMessage = options.i18n.asyncFormFailure;
} }
processResponse({ content: failureMessage }); processResponse({ content: failureMessage });
formElement.classList.remove(ASYNC_FORM_LOADING_CLASS); element.classList.remove(ASYNC_FORM_LOADING_CLASS);
}); });
} }
setup(); return init();
return {
scope: formElement,
destroy: function() {},
};
}; };
if (UtilRegistry) {
UtilRegistry.register({
name: ASYNC_FORM_UTIL_NAME,
selector: ASYNC_FORM_UTIL_SELECTOR,
setup: asyncFormUtil
});
}
})(); })();