PHP Classes

File: modules/system/assets/js/snowboard/extras/FormValidation.js

Recommend this page to a friend!
  Packages of Luke Towers   Winter   modules/system/assets/js/snowboard/extras/FormValidation.js   Download  
File: modules/system/assets/js/snowboard/extras/FormValidation.js
Role: Auxiliary data
Content type: text/plain
Description: Auxiliary data
Class: Winter
Content management system that uses MVC
Author: By
Last change:
Date: 7 months ago
Size: 7,043 bytes
 

Contents

Class file image Download
import Singleton from '../abstracts/Singleton'; /** * Adds AJAX-driven form validation to Snowboard requests. * * Documentation for this feature can be found here: * https://wintercms.com/docs/snowboard/extras#ajax-validation * * @copyright 2022 Winter. * @author Ben Thomson <git@alfreido.com> */ export default class FormValidation extends Singleton { /** * Constructor. */ construct() { this.errorBags = []; } /** * Defines listeners. * * @returns {Object} */ listens() { return { ready: 'ready', ajaxStart: 'clearValidation', ajaxValidationErrors: 'doValidation', }; } /** * Ready event handler. */ ready() { this.collectErrorBags(document); } /** * Retrieves validation errors from an AJAX response and passes them through to the error bags. * * This handler returns false to cancel any further validation handling, and prevents the flash * message that is displayed by default for field errors in AJAX requests from showing. * * @param {HTMLFormElement} form * @param {Object} invalidFields * @param {Request} request * @returns {Boolean} */ doValidation(form, invalidFields, request) { if (request.element && request.element.dataset.requestValidate === undefined) { return null; } if (!form) { return null; } const errorBags = this.errorBags.filter((errorBag) => errorBag.form === form); errorBags.forEach((errorBag) => { this.showErrorBag(errorBag, invalidFields); }); return false; } /** * Clears any validation errors in the given form. * * @param {Promise} promise * @param {Request} request * @returns {void} */ clearValidation(promise, request) { if (request.element && request.element.dataset.requestValidate === undefined) { return; } if (!request.form) { return; } const errorBags = this.errorBags.filter((errorBag) => errorBag.form === request.form); errorBags.forEach((errorBag) => { this.hideErrorBag(errorBag); }); } /** * Collects error bags (elements with "data-validate-error" attribute) and links them to a * placeholder and form. * * The error bags will be initially hidden, and will only show when validation errors occur. * * @param {HTMLElement} rootNode */ collectErrorBags(rootNode) { rootNode.querySelectorAll('[data-validate-error], [data-validate-for]').forEach((errorBag) => { const form = errorBag.closest('form[data-request-validate]'); // If this error bag does not reside within a validating form, remove it if (!form) { errorBag.parentNode.removeChild(errorBag); return; } // Find message list node, if available let messageListElement = null; if (errorBag.matches('[data-validate-error]')) { messageListElement = errorBag.querySelector('[data-message]'); } // Create a placeholder node const placeholder = document.createComment(''); // Register error bag and replace with placeholder const errorBagData = { element: errorBag, form, validateFor: (errorBag.dataset.validateFor) ? errorBag.dataset.validateFor.split(/\s*,\s*/) : '*', placeholder, messageListElement: (messageListElement) ? messageListElement.cloneNode(true) : null, messageListAnchor: null, customMessage: (errorBag.dataset.validateFor) ? (errorBag.textContent !== '' || errorBag.childNodes.length > 0) : false, }; // If an message list element exists, create another placeholder to act as an anchor point if (messageListElement) { const messageListAnchor = document.createComment(''); messageListElement.parentNode.replaceChild(messageListAnchor, messageListElement); errorBagData.messageListAnchor = messageListAnchor; } errorBag.parentNode.replaceChild(placeholder, errorBag); this.errorBags.push(errorBagData); }); } /** * Hides an error bag, replacing the error messages with a placeholder node. * * @param {Object} errorBag */ hideErrorBag(errorBag) { if (errorBag.element.isConnected) { errorBag.element.parentNode.replaceChild(errorBag.placeholder, errorBag.element); } } /** * Shows an error bag with the given invalid fields. * * @param {Object} errorBag * @param {Object} invalidFields */ showErrorBag(errorBag, invalidFields) { if (!this.errorBagValidatesField(errorBag, invalidFields)) { return; } if (!errorBag.element.isConnected) { errorBag.placeholder.parentNode.replaceChild(errorBag.element, errorBag.placeholder); } if (errorBag.validateFor !== '*') { if (!errorBag.customMessage) { const firstField = Object.keys(invalidFields) .filter((field) => errorBag.validateFor.includes(field)) .shift(); [errorBag.element.innerHTML] = invalidFields[firstField]; } } else if (errorBag.messageListElement) { // Remove previous error messages errorBag.element.querySelectorAll('[data-validation-message]').forEach((message) => { message.parentNode.removeChild(message); }); Object.entries(invalidFields).forEach((entry) => { const [, errors] = entry; errors.forEach((error) => { const messageElement = errorBag.messageListElement.cloneNode(true); messageElement.dataset.validationMessage = ''; messageElement.innerHTML = error; errorBag.messageListAnchor.after(messageElement); }); }); } else { [errorBag.element.innerHTML] = invalidFields[Object.keys(invalidFields).shift()]; } } /** * Determines if a given error bag applies for the given invalid fields. * * @param {Object} errorBag * @param {Object} invalidFields * @returns {Boolean} */ errorBagValidatesField(errorBag, invalidFields) { if (errorBag.validateFor === '*') { return true; } return Object.keys(invalidFields) .filter((field) => errorBag.validateFor.includes(field)) .length > 0; } }