Replaced the function-based JavaScript with the object-oriented AccountForm class for the currency form.
This commit is contained in:
parent
0a658a76e8
commit
2b84f64554
@ -24,152 +24,151 @@
|
||||
|
||||
// Initializes the page JavaScript.
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
document.getElementById("accounting-code")
|
||||
.onchange = validateCode;
|
||||
document.getElementById("accounting-name")
|
||||
.onchange = validateName;
|
||||
document.getElementById("accounting-form")
|
||||
.onsubmit = validateForm;
|
||||
CurrencyForm.initialize();
|
||||
});
|
||||
|
||||
/**
|
||||
* The asynchronous validation result
|
||||
* @type {object}
|
||||
* The currency form.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
let isAsyncValid = {};
|
||||
class CurrencyForm {
|
||||
|
||||
/**
|
||||
/**
|
||||
* The form.
|
||||
* @type {HTMLFormElement}
|
||||
*/
|
||||
#formElement;
|
||||
|
||||
/**
|
||||
* The code
|
||||
* @type {HTMLInputElement}
|
||||
*/
|
||||
#code;
|
||||
|
||||
/**
|
||||
* The error message of the code
|
||||
* @type {HTMLDivElement}
|
||||
*/
|
||||
#codeError;
|
||||
|
||||
/**
|
||||
* The name
|
||||
* @type {HTMLInputElement}
|
||||
*/
|
||||
#name;
|
||||
|
||||
/**
|
||||
* The error message of the name
|
||||
* @type {HTMLDivElement}
|
||||
*/
|
||||
#nameError;
|
||||
|
||||
/**
|
||||
* Constructs the currency form.
|
||||
*
|
||||
*/
|
||||
constructor() {
|
||||
this.#formElement = document.getElementById("accounting-form");
|
||||
this.#code = document.getElementById("accounting-code");
|
||||
this.#codeError = document.getElementById("accounting-code-error");
|
||||
this.#name = document.getElementById("accounting-name");
|
||||
this.#nameError = document.getElementById("accounting-name-error");
|
||||
this.#code.onchange = () => {
|
||||
this.#validateCode().then();
|
||||
};
|
||||
this.#name.onchange = () => {
|
||||
this.#validateName();
|
||||
};
|
||||
this.#formElement.onsubmit = () => {
|
||||
this.#validateForm().then((isValid) => {
|
||||
if (isValid) {
|
||||
this.#formElement.submit();
|
||||
}
|
||||
});
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the form.
|
||||
*
|
||||
* @returns {boolean} true if valid, or false otherwise
|
||||
* @private
|
||||
* @returns {Promise<boolean>} true if valid, or false otherwise
|
||||
*/
|
||||
function validateForm() {
|
||||
isAsyncValid = {
|
||||
"code": false,
|
||||
"_sync": false,
|
||||
};
|
||||
async #validateForm() {
|
||||
let isValid = true;
|
||||
isValid = validateCode() && isValid;
|
||||
isValid = validateName() && isValid;
|
||||
isAsyncValid["_sync"] = isValid;
|
||||
submitFormIfAllAsyncValid();
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Submits the form if the whole form passed the asynchronous
|
||||
* validations.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
function submitFormIfAllAsyncValid() {
|
||||
let isValid = true;
|
||||
for (const key of Object.keys(isAsyncValid)) {
|
||||
isValid = isAsyncValid[key] && isValid;
|
||||
isValid = await this.#validateCode() && isValid;
|
||||
isValid = this.#validateName() && isValid;
|
||||
return isValid;
|
||||
}
|
||||
if (isValid) {
|
||||
document.getElementById("accounting-form").submit()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* Validates the code.
|
||||
*
|
||||
* @param changeEvent {Event} the change event, if invoked from onchange
|
||||
* @returns {boolean} true if valid, or false otherwise
|
||||
* @private
|
||||
* @returns {Promise<boolean>} true if valid, or false otherwise
|
||||
*/
|
||||
function validateCode(changeEvent = null) {
|
||||
const key = "code";
|
||||
const isSubmission = changeEvent === null;
|
||||
let hasAsyncValidation = false;
|
||||
const field = document.getElementById("accounting-code");
|
||||
const error = document.getElementById("accounting-code-error");
|
||||
field.value = field.value.trim();
|
||||
if (field.value === "") {
|
||||
field.classList.add("is-invalid");
|
||||
error.innerText = A_("Please fill in the code.");
|
||||
async #validateCode(changeEvent = null) {
|
||||
this.#code.value = this.#code.value.trim();
|
||||
if (this.#code.value === "") {
|
||||
this.#code.classList.add("is-invalid");
|
||||
this.#codeError.innerText = A_("Please fill in the code.");
|
||||
return false;
|
||||
}
|
||||
const blocklist = JSON.parse(field.dataset.blocklist);
|
||||
if (blocklist.includes(field.value)) {
|
||||
field.classList.add("is-invalid");
|
||||
error.innerText = A_("This code is not available.");
|
||||
const blocklist = JSON.parse(this.#code.dataset.blocklist);
|
||||
if (blocklist.includes(this.#code.value)) {
|
||||
this.#code.classList.add("is-invalid");
|
||||
this.#codeError.innerText = A_("This code is not available.");
|
||||
return false;
|
||||
}
|
||||
if (!field.value.match(/^[A-Z]{3}$/)) {
|
||||
field.classList.add("is-invalid");
|
||||
error.innerText = A_("Code can only be composed of 3 upper-cased letters.");
|
||||
if (!this.#code.value.match(/^[A-Z]{3}$/)) {
|
||||
this.#code.classList.add("is-invalid");
|
||||
this.#codeError.innerText = A_("Code can only be composed of 3 upper-cased letters.");
|
||||
return false;
|
||||
}
|
||||
const original = field.dataset.original;
|
||||
if (original === "" || field.value !== original) {
|
||||
hasAsyncValidation = true;
|
||||
validateAsyncCodeIsDuplicated(isSubmission, key);
|
||||
const original = this.#code.dataset.original;
|
||||
if (original === "" || this.#code.value !== original) {
|
||||
const response = await fetch(this.#code.dataset.existsUrl + "?q=" + encodeURIComponent(this.#code.value));
|
||||
const data = await response.json();
|
||||
if (data["exists"]) {
|
||||
this.#code.classList.add("is-invalid");
|
||||
this.#codeError.innerText = A_("Code conflicts with another currency.");
|
||||
return false;
|
||||
}
|
||||
if (!hasAsyncValidation) {
|
||||
isAsyncValid[key] = true;
|
||||
field.classList.remove("is-invalid");
|
||||
error.innerText = "";
|
||||
}
|
||||
this.#code.classList.remove("is-invalid");
|
||||
this.#codeError.innerText = "";
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates asynchronously whether the code is duplicated.
|
||||
* The boolean validation result is stored in isAsyncValid[key].
|
||||
*
|
||||
* @param isSubmission {boolean} whether this is invoked from a form submission
|
||||
* @param key {string} the key to store the result in isAsyncValid
|
||||
* @private
|
||||
*/
|
||||
function validateAsyncCodeIsDuplicated(isSubmission, key) {
|
||||
const field = document.getElementById("accounting-code");
|
||||
const error = document.getElementById("accounting-code-error");
|
||||
const url = field.dataset.existsUrl;
|
||||
const onLoad = function () {
|
||||
if (this.status === 200) {
|
||||
const result = JSON.parse(this.responseText);
|
||||
if (result["exists"]) {
|
||||
field.classList.add("is-invalid");
|
||||
error.innerText = A_("Code conflicts with another currency.");
|
||||
if (isSubmission) {
|
||||
isAsyncValid[key] = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
field.classList.remove("is-invalid");
|
||||
error.innerText = "";
|
||||
if (isSubmission) {
|
||||
isAsyncValid[key] = true;
|
||||
submitFormIfAllAsyncValid();
|
||||
}
|
||||
}
|
||||
};
|
||||
const request = new XMLHttpRequest();
|
||||
request.onload = onLoad;
|
||||
request.open("GET", url + "?q=" + encodeURIComponent(field.value));
|
||||
request.send();
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* Validates the name.
|
||||
*
|
||||
* @returns {boolean} true if valid, or false otherwise
|
||||
* @private
|
||||
*/
|
||||
function validateName() {
|
||||
const field = document.getElementById("accounting-name");
|
||||
const error = document.getElementById("accounting-name-error");
|
||||
field.value = field.value.trim();
|
||||
if (field.value === "") {
|
||||
field.classList.add("is-invalid");
|
||||
error.innerText = A_("Please fill in the name.");
|
||||
#validateName() {
|
||||
this.#name.value = this.#name.value.trim();
|
||||
if (this.#name.value === "") {
|
||||
this.#name.classList.add("is-invalid");
|
||||
this.#nameError.innerText = A_("Please fill in the name.");
|
||||
return false;
|
||||
}
|
||||
field.classList.remove("is-invalid");
|
||||
error.innerText = "";
|
||||
this.#name.classList.remove("is-invalid");
|
||||
this.#nameError.innerText = "";
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* The form
|
||||
* @type {CurrencyForm}
|
||||
*/
|
||||
static #form;
|
||||
|
||||
/**
|
||||
* Initializes the currency form.
|
||||
*
|
||||
*/
|
||||
static initialize() {
|
||||
this.#form = new CurrencyForm();
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user