chg: [js:api-helper] Added documentation
parent
5782e29642
commit
0347d7dfda
|
@ -110,7 +110,7 @@ class AppController extends Controller
|
||||||
$this->ACL->checkAccess();
|
$this->ACL->checkAccess();
|
||||||
$this->set('menu', $this->ACL->getMenu());
|
$this->set('menu', $this->ACL->getMenu());
|
||||||
$this->set('ajax', $this->request->is('ajax'));
|
$this->set('ajax', $this->request->is('ajax'));
|
||||||
if (!empty($this->request->getHeader('X-Request-Html-On-Failure'))) {
|
if (!empty($this->request->getHeader('X-Force-HTML-On-Validation-Failure'))) {
|
||||||
$this->ajax_with_html_on_failure = true;
|
$this->ajax_with_html_on_failure = true;
|
||||||
}
|
}
|
||||||
$this->request->getParam('prefix');
|
$this->request->getParam('prefix');
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
/** AJAXApi class providing helpers to perform AJAX request */
|
||||||
class AJAXApi {
|
class AJAXApi {
|
||||||
static genericRequestHeaders = {
|
static genericRequestHeaders = {
|
||||||
'X-Requested-With': 'XMLHttpRequest'
|
'X-Requested-With': 'XMLHttpRequest'
|
||||||
|
@ -11,14 +12,21 @@ class AJAXApi {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
}
|
}
|
||||||
static renderHTMLOnFailureHeader = {
|
static renderHTMLOnFailureHeader = {
|
||||||
name: 'X-Request-HTML-On-Failure',
|
name: 'X-Force-HTML-On-Validation-Failure',
|
||||||
value: '1'
|
value: '1'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @namespace
|
||||||
|
* @property {boolean} provideFeedback - The ID to be used for the toast's container
|
||||||
|
* @property {(jQuery|string)} statusNode - The node on which the loading overlay should be placed (OverlayFactory.node)
|
||||||
|
* @property {boolean} forceHTMLOnValidationFailure - If true, attach a special header to ask for HTML instead of JSON in case of form validation failure
|
||||||
|
* @property {Object} errorToast - The options supported by Toaster#defaultOptions
|
||||||
|
*/
|
||||||
static defaultOptions = {
|
static defaultOptions = {
|
||||||
provideFeedback: true,
|
provideFeedback: true,
|
||||||
statusNode: false,
|
statusNode: false,
|
||||||
renderedHTMLOnFailureRequested: false,
|
forceHTMLOnValidationFailure: false,
|
||||||
errorToast: {
|
errorToast: {
|
||||||
delay: 10000
|
delay: 10000
|
||||||
}
|
}
|
||||||
|
@ -26,11 +34,21 @@ class AJAXApi {
|
||||||
options = {}
|
options = {}
|
||||||
loadingOverlay = false
|
loadingOverlay = false
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instantiate an AJAXApi object.
|
||||||
|
* @param {Object} options - The options supported by AJAXApi#defaultOptions
|
||||||
|
*/
|
||||||
constructor(options) {
|
constructor(options) {
|
||||||
this.mergeOptions(AJAXApi.defaultOptions)
|
this.mergeOptions(AJAXApi.defaultOptions)
|
||||||
this.mergeOptions(options)
|
this.mergeOptions(options)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Based on the current configuration, provide feedback to the user via toast, console or do not
|
||||||
|
* @param {Object} toastOptions - The options supported by Toaster#defaultOptions
|
||||||
|
* @param {boolean} isError - If true and toast feedback is disable, write the feedback in the console
|
||||||
|
* @param {boolean} skip - If true, skip the feedback regardless of the configuration
|
||||||
|
*/
|
||||||
provideFeedback(toastOptions, isError=false, skip=false) {
|
provideFeedback(toastOptions, isError=false, skip=false) {
|
||||||
const alteredToastOptions = isError ? Object.assign({}, AJAXApi.defaultOptions.errorToast, toastOptions) : toastOptions
|
const alteredToastOptions = isError ? Object.assign({}, AJAXApi.defaultOptions.errorToast, toastOptions) : toastOptions
|
||||||
if (!skip) {
|
if (!skip) {
|
||||||
|
@ -44,10 +62,20 @@ class AJAXApi {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Merge newOptions configuration into the current object
|
||||||
|
* @param {Object} The options supported by AJAXApi#defaultOptions
|
||||||
|
*/
|
||||||
mergeOptions(newOptions) {
|
mergeOptions(newOptions) {
|
||||||
this.options = Object.assign({}, this.options, newOptions)
|
this.options = Object.assign({}, this.options, newOptions)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {FormData} formData - The data of a form
|
||||||
|
* @param {Object} dataToMerge - Data to be merge into formData
|
||||||
|
* @return {FormData} The form data merged with the additional dataToMerge data
|
||||||
|
*/
|
||||||
static mergeFormData(formData, dataToMerge) {
|
static mergeFormData(formData, dataToMerge) {
|
||||||
for (const [fieldName, value] of Object.entries(dataToMerge)) {
|
for (const [fieldName, value] of Object.entries(dataToMerge)) {
|
||||||
formData.set(fieldName, value)
|
formData.set(fieldName, value)
|
||||||
|
@ -55,30 +83,57 @@ class AJAXApi {
|
||||||
return formData
|
return formData
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} url - The URL to fetch
|
||||||
|
* @param {Object} [options={}] - The options supported by AJAXApi#defaultOptions
|
||||||
|
* @return {Promise<string>} Promise object resolving to the fetched HTML
|
||||||
|
*/
|
||||||
static async quickFetchURL(url, options={}) {
|
static async quickFetchURL(url, options={}) {
|
||||||
const constAlteredOptions = Object.assign({}, {provideFeedback: false}, options)
|
const constAlteredOptions = Object.assign({}, {provideFeedback: false}, options)
|
||||||
const tmpApi = new AJAXApi(constAlteredOptions)
|
const tmpApi = new AJAXApi(constAlteredOptions)
|
||||||
return tmpApi.fetchURL(url, constAlteredOptions.skipRequestHooks)
|
return tmpApi.fetchURL(url, constAlteredOptions.skipRequestHooks)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} url - The URL to fetch
|
||||||
|
* @param {Object} [options={}] - The options supported by AJAXApi#defaultOptions
|
||||||
|
* @return {Promise<HTMLFormElement>} Promise object resolving to the fetched form
|
||||||
|
*/
|
||||||
static async quickFetchForm(url, options={}) {
|
static async quickFetchForm(url, options={}) {
|
||||||
const constAlteredOptions = Object.assign({}, {provideFeedback: false}, options)
|
const constAlteredOptions = Object.assign({}, {provideFeedback: false}, options)
|
||||||
const tmpApi = new AJAXApi(constAlteredOptions)
|
const tmpApi = new AJAXApi(constAlteredOptions)
|
||||||
return tmpApi.fetchForm(url, constAlteredOptions.skipRequestHooks)
|
return tmpApi.fetchForm(url, constAlteredOptions.skipRequestHooks)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {HTMLFormElement} form - The form to be posted
|
||||||
|
* @param {Object} [dataToMerge={}] - Additional data to be integrated or modified in the form
|
||||||
|
* @param {Object} [options={}] - The options supported by AJAXApi#defaultOptions
|
||||||
|
* @return {Promise<Object>} Promise object resolving to the result of the POST operation
|
||||||
|
*/
|
||||||
static async quickPostForm(form, dataToMerge={}, options={}) {
|
static async quickPostForm(form, dataToMerge={}, options={}) {
|
||||||
const constAlteredOptions = Object.assign({}, {}, options)
|
const constAlteredOptions = Object.assign({}, {}, options)
|
||||||
const tmpApi = new AJAXApi(constAlteredOptions)
|
const tmpApi = new AJAXApi(constAlteredOptions)
|
||||||
return tmpApi.postForm(form, dataToMerge, constAlteredOptions.skipRequestHooks)
|
return tmpApi.postForm(form, dataToMerge, constAlteredOptions.skipRequestHooks)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} url - The URL from which to fetch the form
|
||||||
|
* @param {Object} [dataToMerge={}] - Additional data to be integrated or modified in the form
|
||||||
|
* @return {Promise<Object>} Promise object resolving to the result of the POST operation
|
||||||
|
*/
|
||||||
static async quickFetchAndPostForm(url, dataToMerge={}, options={}) {
|
static async quickFetchAndPostForm(url, dataToMerge={}, options={}) {
|
||||||
const constAlteredOptions = Object.assign({}, {}, options)
|
const constAlteredOptions = Object.assign({}, {}, options)
|
||||||
const tmpApi = new AJAXApi(constAlteredOptions)
|
const tmpApi = new AJAXApi(constAlteredOptions)
|
||||||
return tmpApi.fetchAndPostForm(url, dataToMerge, constAlteredOptions.skipRequestHooks)
|
return tmpApi.fetchAndPostForm(url, dataToMerge, constAlteredOptions.skipRequestHooks)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} url - The URL to fetch
|
||||||
|
* @param {boolean} [skipRequestHooks=false] - If true, default request hooks will be skipped
|
||||||
|
* @param {boolean} [skipFeedback=false] - Pass this value to the AJAXApi.provideFeedback function
|
||||||
|
* @return {Promise<string>} Promise object resolving to the fetched HTML
|
||||||
|
*/
|
||||||
async fetchURL(url, skipRequestHooks=false, skipFeedback=false) {
|
async fetchURL(url, skipRequestHooks=false, skipFeedback=false) {
|
||||||
if (!skipRequestHooks) {
|
if (!skipRequestHooks) {
|
||||||
this.beforeRequest()
|
this.beforeRequest()
|
||||||
|
@ -89,12 +144,12 @@ class AJAXApi {
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error('Network response was not ok')
|
throw new Error('Network response was not ok')
|
||||||
}
|
}
|
||||||
const data = await response.text();
|
const dataHtml = await response.text();
|
||||||
this.provideFeedback({
|
this.provideFeedback({
|
||||||
variant: 'success',
|
variant: 'success',
|
||||||
title: 'URL fetched',
|
title: 'URL fetched',
|
||||||
}, false, skipFeedback);
|
}, false, skipFeedback);
|
||||||
toReturn = data;
|
toReturn = dataHtml;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.provideFeedback({
|
this.provideFeedback({
|
||||||
variant: 'danger',
|
variant: 'danger',
|
||||||
|
@ -110,6 +165,12 @@ class AJAXApi {
|
||||||
return toReturn
|
return toReturn
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} url - The URL to fetch
|
||||||
|
* @param {boolean} [skipRequestHooks=false] - If true, default request hooks will be skipped
|
||||||
|
* @param {boolean} [skipFeedback=false] - Pass this value to the AJAXApi.provideFeedback function
|
||||||
|
* @return {Promise<HTMLFormElement>} Promise object resolving to the fetched HTML
|
||||||
|
*/
|
||||||
async fetchForm(url, skipRequestHooks=false, skipFeedback=false) {
|
async fetchForm(url, skipRequestHooks=false, skipFeedback=false) {
|
||||||
if (!skipRequestHooks) {
|
if (!skipRequestHooks) {
|
||||||
this.beforeRequest()
|
this.beforeRequest()
|
||||||
|
@ -143,6 +204,13 @@ class AJAXApi {
|
||||||
return toReturn
|
return toReturn
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {HTMLFormElement} form - The form to be posted
|
||||||
|
* @param {Object} [dataToMerge={}] - Additional data to be integrated or modified in the form
|
||||||
|
* @param {boolean} [skipRequestHooks=false] - If true, default request hooks will be skipped
|
||||||
|
* @param {boolean} [skipFeedback=false] - Pass this value to the AJAXApi.provideFeedback function
|
||||||
|
* @return {Promise<Object>} Promise object resolving to the result of the POST operation
|
||||||
|
*/
|
||||||
async postForm(form, dataToMerge={}, skipRequestHooks=false, skipFeedback=false) {
|
async postForm(form, dataToMerge={}, skipRequestHooks=false, skipFeedback=false) {
|
||||||
if (!skipRequestHooks) {
|
if (!skipRequestHooks) {
|
||||||
this.beforeRequest()
|
this.beforeRequest()
|
||||||
|
@ -154,7 +222,7 @@ class AJAXApi {
|
||||||
let formData = new FormData(form)
|
let formData = new FormData(form)
|
||||||
formData = AJAXApi.mergeFormData(formData, dataToMerge)
|
formData = AJAXApi.mergeFormData(formData, dataToMerge)
|
||||||
let requestConfig = AJAXApi.genericRequestConfigPOST
|
let requestConfig = AJAXApi.genericRequestConfigPOST
|
||||||
if (this.options.renderedHTMLOnFailureRequested) {
|
if (this.options.forceHTMLOnValidationFailure) {
|
||||||
requestConfig.headers.append(AJAXApi.renderHTMLOnFailureHeader.name, AJAXApi.renderHTMLOnFailureHeader.value)
|
requestConfig.headers.append(AJAXApi.renderHTMLOnFailureHeader.name, AJAXApi.renderHTMLOnFailureHeader.value)
|
||||||
}
|
}
|
||||||
let options = {
|
let options = {
|
||||||
|
@ -184,7 +252,7 @@ class AJAXApi {
|
||||||
toReturn = Promise.reject(data.errors);
|
toReturn = Promise.reject(data.errors);
|
||||||
}
|
}
|
||||||
} catch (error) { // could not parse JSON
|
} catch (error) { // could not parse JSON
|
||||||
if (this.options.renderedHTMLOnFailureRequested) {
|
if (this.options.forceHTMLOnValidationFailure) {
|
||||||
const data = await clonedResponse.text();
|
const data = await clonedResponse.text();
|
||||||
toReturn = {
|
toReturn = {
|
||||||
success: 0,
|
success: 0,
|
||||||
|
@ -210,6 +278,12 @@ class AJAXApi {
|
||||||
return toReturn
|
return toReturn
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} url - The URL from which to fetch the form
|
||||||
|
* @param {Object} [dataToMerge={}] - Additional data to be integrated or modified in the form
|
||||||
|
* @param {boolean} [skipRequestHooks=false] - If true, default request hooks will be skipped
|
||||||
|
* @return {Promise<Object>} Promise object resolving to the result of the POST operation
|
||||||
|
*/
|
||||||
async fetchAndPostForm(url, dataToMerge={}, skipRequestHooks=false) {
|
async fetchAndPostForm(url, dataToMerge={}, skipRequestHooks=false) {
|
||||||
if (!skipRequestHooks) {
|
if (!skipRequestHooks) {
|
||||||
this.beforeRequest()
|
this.beforeRequest()
|
||||||
|
@ -228,18 +302,21 @@ class AJAXApi {
|
||||||
return toReturn
|
return toReturn
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Based on the configuration, show the loading overlay */
|
||||||
beforeRequest() {
|
beforeRequest() {
|
||||||
if (this.options.statusNode !== false) {
|
if (this.options.statusNode !== false) {
|
||||||
this.toggleLoading(true)
|
this.toggleLoading(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Based on the configuration, hide the loading overlay */
|
||||||
afterRequest() {
|
afterRequest() {
|
||||||
if (this.options.statusNode !== false) {
|
if (this.options.statusNode !== false) {
|
||||||
this.toggleLoading(false)
|
this.toggleLoading(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Show or hide the loading overlay */
|
||||||
toggleLoading(loading) {
|
toggleLoading(loading) {
|
||||||
if (this.loadingOverlay === false) {
|
if (this.loadingOverlay === false) {
|
||||||
this.loadingOverlay = new OverlayFactory(this.options.statusNode);
|
this.loadingOverlay = new OverlayFactory(this.options.statusNode);
|
||||||
|
|
|
@ -50,7 +50,7 @@ class UIFactory {
|
||||||
* @param {string} url - The URL from which the $container's content should be fetched
|
* @param {string} url - The URL from which the $container's content should be fetched
|
||||||
* @param {(jQuery|string)} $container - The container that should hold the data fetched
|
* @param {(jQuery|string)} $container - The container that should hold the data fetched
|
||||||
* @param {(jQuery|string)} [$statusNode=null] - A reference to a HTML node on which the loading animation should be displayed. If not provided, $container will be used
|
* @param {(jQuery|string)} [$statusNode=null] - A reference to a HTML node on which the loading animation should be displayed. If not provided, $container will be used
|
||||||
* @return {Promise<Object>} Promise object resolving to the $container object after its content has been replaced
|
* @return {Promise<jQuery>} Promise object resolving to the $container object after its content has been replaced
|
||||||
*/
|
*/
|
||||||
reload (url, $container, $statusNode=null) {
|
reload (url, $container, $statusNode=null) {
|
||||||
$container = $($container)
|
$container = $($container)
|
||||||
|
@ -514,7 +514,7 @@ class ModalFactory {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.options.APIConfirm = (tmpApi) => {
|
this.options.APIConfirm = (tmpApi) => {
|
||||||
tmpApi.mergeOptions({renderedHTMLOnFailureRequested: true})
|
tmpApi.mergeOptions({forceHTMLOnValidationFailure: true})
|
||||||
return tmpApi.postForm($form[0])
|
return tmpApi.postForm($form[0])
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
if (data.success) {
|
if (data.success) {
|
||||||
|
|
Loading…
Reference in New Issue