Js: add live field validation (#75724)
gitea/wcs/pipeline/head There was a failure building this commit Details

This commit is contained in:
Thomas Jund 2023-04-13 15:43:44 +02:00
parent 4faff356ed
commit 04a3dedd08
1 changed files with 152 additions and 0 deletions

View File

@ -774,3 +774,155 @@ function check_condition(button, widget_id) {
},
});
}
/*
* 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 validy of field by HTML attributs
* cf JS constaint validation API
* return first error fouded
*/
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 Validy of field by request to server
*/
const hasServerError = function (field, form, url) {
return fetch( url+field.name, {
method: 'POST',
body: new FormData(form)
})
.then( (response) => response.json() )
.then( (json) => {
if (json.err === 0) {
return
} else {
let errorType
for (const key in json) {
if (json[key] === true) {
errorType = key
break
}
}
return errorType
}
})
}
class FieldLiveValidation {
constructor (widget, formDatas) {
this.widget = widget
this.name = widget.dataset.widgetName
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);
const serverError = this.widget.dataset.supportsCheckCondition
? await hasServerError(field, field.form, this.checkUrl)
: false
const error = attrError ? attrError : serverError
if (error) {
this.showError(field, error)
} else {
this.removeError(field, error)
}
}
showError(field, error) {
if (this.hasError === error) return
this.widget.classList.add(this.errorClass)
const errorElMessage = document.getElementById(`error_${this.name}_${error}`).innerHTML
this.errorEl.innerHTML = errorElMessage
this.widget.appendChild(this.errorEl)
field.setAttribute("aria-invalid", "true")
field.setAttribute("aria-describedby", this.errorEl.id)
this.hasError = error
}
removeError(field, error) {
if (error) 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 allready on error
if (this.widget.classList.contains(this.errorClass)) {
this.hasError = true;
// Check if error element exist allready
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 change 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-check-condition-url]')
const formWidgets = form.querySelectorAll('.widget')
let formDatas = {
errorTpl: document.getElementById('form_error_tpl'),
checkUrl: form.dataset.checkConditionUrl + '?field=',
}
formWidgets.forEach((widget) => {
new LiveValidation(widget, formDatas)
})
})