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.
|
// Initializes the page JavaScript.
|
||||||
document.addEventListener("DOMContentLoaded", () => {
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
document.getElementById("accounting-code")
|
CurrencyForm.initialize();
|
||||||
.onchange = validateCode;
|
|
||||||
document.getElementById("accounting-name")
|
|
||||||
.onchange = validateName;
|
|
||||||
document.getElementById("accounting-form")
|
|
||||||
.onsubmit = validateForm;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The asynchronous validation result
|
* The currency form.
|
||||||
* @type {object}
|
*
|
||||||
* @private
|
* @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.
|
* Validates the form.
|
||||||
*
|
*
|
||||||
* @returns {boolean} true if valid, or false otherwise
|
* @returns {Promise<boolean>} true if valid, or false otherwise
|
||||||
* @private
|
|
||||||
*/
|
*/
|
||||||
function validateForm() {
|
async #validateForm() {
|
||||||
isAsyncValid = {
|
|
||||||
"code": false,
|
|
||||||
"_sync": false,
|
|
||||||
};
|
|
||||||
let isValid = true;
|
let isValid = true;
|
||||||
isValid = validateCode() && isValid;
|
isValid = await this.#validateCode() && isValid;
|
||||||
isValid = validateName() && isValid;
|
isValid = this.#validateName() && isValid;
|
||||||
isAsyncValid["_sync"] = isValid;
|
return 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;
|
|
||||||
}
|
|
||||||
if (isValid) {
|
|
||||||
document.getElementById("accounting-form").submit()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validates the code.
|
* Validates the code.
|
||||||
*
|
*
|
||||||
* @param changeEvent {Event} the change event, if invoked from onchange
|
* @param changeEvent {Event} the change event, if invoked from onchange
|
||||||
* @returns {boolean} true if valid, or false otherwise
|
* @returns {Promise<boolean>} true if valid, or false otherwise
|
||||||
* @private
|
|
||||||
*/
|
*/
|
||||||
function validateCode(changeEvent = null) {
|
async #validateCode(changeEvent = null) {
|
||||||
const key = "code";
|
this.#code.value = this.#code.value.trim();
|
||||||
const isSubmission = changeEvent === null;
|
if (this.#code.value === "") {
|
||||||
let hasAsyncValidation = false;
|
this.#code.classList.add("is-invalid");
|
||||||
const field = document.getElementById("accounting-code");
|
this.#codeError.innerText = A_("Please fill in the 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.");
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const blocklist = JSON.parse(field.dataset.blocklist);
|
const blocklist = JSON.parse(this.#code.dataset.blocklist);
|
||||||
if (blocklist.includes(field.value)) {
|
if (blocklist.includes(this.#code.value)) {
|
||||||
field.classList.add("is-invalid");
|
this.#code.classList.add("is-invalid");
|
||||||
error.innerText = A_("This code is not available.");
|
this.#codeError.innerText = A_("This code is not available.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!field.value.match(/^[A-Z]{3}$/)) {
|
if (!this.#code.value.match(/^[A-Z]{3}$/)) {
|
||||||
field.classList.add("is-invalid");
|
this.#code.classList.add("is-invalid");
|
||||||
error.innerText = A_("Code can only be composed of 3 upper-cased letters.");
|
this.#codeError.innerText = A_("Code can only be composed of 3 upper-cased letters.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const original = field.dataset.original;
|
const original = this.#code.dataset.original;
|
||||||
if (original === "" || field.value !== original) {
|
if (original === "" || this.#code.value !== original) {
|
||||||
hasAsyncValidation = true;
|
const response = await fetch(this.#code.dataset.existsUrl + "?q=" + encodeURIComponent(this.#code.value));
|
||||||
validateAsyncCodeIsDuplicated(isSubmission, key);
|
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;
|
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.
|
* Validates the name.
|
||||||
*
|
*
|
||||||
* @returns {boolean} true if valid, or false otherwise
|
* @returns {boolean} true if valid, or false otherwise
|
||||||
* @private
|
|
||||||
*/
|
*/
|
||||||
function validateName() {
|
#validateName() {
|
||||||
const field = document.getElementById("accounting-name");
|
this.#name.value = this.#name.value.trim();
|
||||||
const error = document.getElementById("accounting-name-error");
|
if (this.#name.value === "") {
|
||||||
field.value = field.value.trim();
|
this.#name.classList.add("is-invalid");
|
||||||
if (field.value === "") {
|
this.#nameError.innerText = A_("Please fill in the name.");
|
||||||
field.classList.add("is-invalid");
|
|
||||||
error.innerText = A_("Please fill in the name.");
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
field.classList.remove("is-invalid");
|
this.#name.classList.remove("is-invalid");
|
||||||
error.innerText = "";
|
this.#nameError.innerText = "";
|
||||||
return true;
|
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