Js: add live field validation (#75724)
gitea/wcs/pipeline/head There was a failure building this commit
Details
gitea/wcs/pipeline/head There was a failure building this commit
Details
This commit is contained in:
parent
4faff356ed
commit
04a3dedd08
|
@ -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)
|
||||
})
|
||||
})
|
||||
|
|
Loading…
Reference in New Issue