This commit is contained in:
parent
a4a7b4c97f
commit
2d784e000a
|
@ -756,3 +756,168 @@ $(function() {
|
|||
});
|
||||
}
|
||||
});
|
||||
|
||||
/*
|
||||
* Live Field Validation
|
||||
*/
|
||||
|
||||
const LiveValidation = (function(){
|
||||
|
||||
const excludedField = function (field) {
|
||||
if (field.disabled ) return true
|
||||
const excludedType = [ 'button', 'reset', 'submit' ]
|
||||
if (excludedType.includes(field.type)) return true
|
||||
return false
|
||||
}
|
||||
|
||||
/*
|
||||
* Check validity of field by HTML attributes
|
||||
* cf JS constraint validation API
|
||||
* return first error found
|
||||
*/
|
||||
const hasAttrError = function (field) {
|
||||
const validityState = field.validity
|
||||
if (validityState.valid) return
|
||||
|
||||
let errorType
|
||||
for (const key in validityState) {
|
||||
if (validityState[key]) {
|
||||
errorType = key
|
||||
break
|
||||
}
|
||||
}
|
||||
return errorType
|
||||
}
|
||||
|
||||
/*
|
||||
* Check validity of field by request to server
|
||||
*/
|
||||
const hasServerError = async function (name, field, form, url) {
|
||||
const response = await fetch( url+name, {
|
||||
method: 'POST',
|
||||
body: new FormData(form)
|
||||
})
|
||||
|
||||
json = await response.json()
|
||||
|
||||
if (json.err !== 1) {
|
||||
return
|
||||
} else {
|
||||
let errorType
|
||||
for (const key in json) {
|
||||
if (json[key] === true) {
|
||||
errorType = key
|
||||
break
|
||||
}
|
||||
}
|
||||
return [errorType, json.msg]
|
||||
}
|
||||
}
|
||||
|
||||
class FieldLiveValidation {
|
||||
constructor (widget, formDatas) {
|
||||
this.widget = widget
|
||||
this.name = widget.dataset.widgetNameForId
|
||||
this.errorClass = "widget-with-error"
|
||||
this.errorEl = this.setErrorEl(formDatas.errorTpl.content.children[0])
|
||||
this.checkUrl = formDatas.checkUrl
|
||||
this.hasError = false
|
||||
this.init()
|
||||
}
|
||||
|
||||
setErrorEl = function(errorTpl) {
|
||||
const errorEl = document.importNode(errorTpl)
|
||||
errorEl.id = errorEl.id.replace('fieldname', this.name)
|
||||
return errorEl
|
||||
}
|
||||
|
||||
async toggleStatus(field) {
|
||||
if (excludedField(field)) return
|
||||
const attrError = hasAttrError(field)
|
||||
if(attrError) {
|
||||
this.showError(field, attrError)
|
||||
}
|
||||
else {
|
||||
const serverError = this.widget.dataset.supportsLiveValidation
|
||||
? await hasServerError(this.name, field, field.form, this.checkUrl)
|
||||
: false
|
||||
if (serverError) {
|
||||
const [error, overrideMsg] = serverError
|
||||
this.showError(field, error, overrideMsg)
|
||||
}
|
||||
else {
|
||||
this.removeError(field)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
showError(field, error, overrideMsg) {
|
||||
if(!this.hasError) {
|
||||
this.widget.classList.add(this.errorClass)
|
||||
this.widget.appendChild(this.errorEl)
|
||||
field.setAttribute("aria-invalid", "true")
|
||||
field.setAttribute("aria-describedby", this.errorEl.id)
|
||||
}
|
||||
|
||||
const errorElMessage = document.getElementById(`error_${this.name}_${error}`).innerHTML
|
||||
this.errorEl.innerHTML = errorElMessage
|
||||
if(overrideMsg) {
|
||||
const errorMessageContainer = this.errorEl.querySelector(`#error_${this.name}_${error}_message`)
|
||||
errorMessageContainer.innerHTML = overrideMsg
|
||||
}
|
||||
this.hasError = error
|
||||
}
|
||||
|
||||
removeError(field) {
|
||||
if(!this.hasError) {
|
||||
return
|
||||
}
|
||||
|
||||
this.errorEl.remove()
|
||||
field.setAttribute("aria-invalid", "false")
|
||||
field.setAttribute("aria-describedby", this.errorEl.id)
|
||||
this.widget.classList.remove(this.errorClass)
|
||||
this.hasError = false
|
||||
}
|
||||
|
||||
init() {
|
||||
// Check if field is already on error
|
||||
if (this.widget.classList.contains(this.errorClass)) {
|
||||
this.hasError = true;
|
||||
// Check if error element exist already
|
||||
const existingErrorEl = document.getElementById(this.errorEl.id)
|
||||
if (existingErrorEl)
|
||||
this.errorEl = existingErrorEl;
|
||||
}
|
||||
|
||||
// Events
|
||||
this.widget.addEventListener('blur', (event) => {
|
||||
this.toggleStatus(event.target)
|
||||
}, true);
|
||||
// If field has Error, check when it changes with debounce
|
||||
let timeout;
|
||||
this.widget.addEventListener('input', (event) => {
|
||||
if (this.hasError) {
|
||||
clearTimeout(timeout)
|
||||
timeout = setTimeout(() => {
|
||||
this.toggleStatus(event.target)
|
||||
}, 500)
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
}
|
||||
|
||||
return FieldLiveValidation
|
||||
})()
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function(){
|
||||
const form = document.querySelector('form[data-live-validation-url]')
|
||||
const formWidgets = form.querySelectorAll('.widget:not(.BlockWidget):not(.BlockSubWidget)')
|
||||
let formDatas = {
|
||||
errorTpl: document.getElementById('form_error_tpl'),
|
||||
checkUrl: form.dataset.liveValidationUrl + '?field=',
|
||||
}
|
||||
formWidgets.forEach((widget) => {
|
||||
new LiveValidation(widget, formDatas)
|
||||
})
|
||||
})
|
||||
|
|
|
@ -53,7 +53,7 @@
|
|||
{% if widget.supports_live_validation and not widget.readonly %}
|
||||
{% block widget-error-templates %}
|
||||
{% for error_message in widget.get_error_messages %}
|
||||
<template id="error_{{ widget.get_name_for_id }}_{{ error_message.camel_code }}"><p>{{ error_message.message }}</p></template>
|
||||
<template id="error_{{ widget.get_name_for_id }}_{{ error_message.camel_code }}"><p id="error_{{ widget.get_name_for_id }}_{{ error_message.camel_code }}_message">{{ error_message.message }}</p></template>
|
||||
{% endfor %}
|
||||
{% endblock %}
|
||||
{% endif %}
|
||||
|
|
Loading…
Reference in New Issue