Discard `invalid` events for hidden form builder form fields
authorMatthias Schmidt <gravatronics@live.com>
Sun, 25 Mar 2018 14:35:33 +0000 (16:35 +0200)
committerMatthias Schmidt <gravatronics@live.com>
Sun, 25 Mar 2018 14:35:33 +0000 (16:35 +0200)
See #2509

wcfsetup/install/files/acp/templates/__form.tpl
wcfsetup/install/files/js/WoltLabSuite/Core/Form/Builder/Field/Dependency/Manager.js

index a8ad558bb958704d6a4f34792434ed9f40148593..5f50ac22b6a2f089b12960cf077a3f366f43df0e 100644 (file)
@@ -1,3 +1,10 @@
+<script data-relocate="true">
+       {* register form with dependency manager before any form field-related JavaScript code is executed below *}
+       require(['WoltLabSuite/Core/Form/Builder/Field/Dependency/Manager'], function(FormBuilderFieldDependencyManager) {
+               FormBuilderFieldDependencyManager.register('{@$form->getId()}');
+       });
+</script>
+
 <form method="{@$form->getMethod()}" action="{@$form->getAction()}" id="{@$form->getId()}"{if !$form->getClasses()|empty} class="{implode from=$form->getClasses() item='class'}{$class}{/implode}"{/if}{foreach from=$form->getAttributes() key='attributeName' item='attributeValue'} {$attributeName}="{$attributeValue}"{/foreach}>
        {foreach from=$form item='child'}
                {if $child->isAvailable()}
                {@SECURITY_TOKEN_INPUT_TAG}
        </div>
 </form>
+
+<script data-relocate="true">
+       {* after all dependencies have been added, check them *}
+       require(['WoltLabSuite/Core/Form/Builder/Field/Dependency/Manager'], function(FormBuilderFieldDependencyManager) {
+               FormBuilderFieldDependencyManager.checkDependencies();
+       });
+</script>
index 6084f4a6e79cd4aed1a5b3ec931f1a20e8e080c0..95c86f64be6164b9423f4831d4277f1735e24afb 100644 (file)
@@ -7,7 +7,7 @@
  * @module     WoltLabSuite/Core/Form/Builder/Field/Dependency/Manager
  * @since      3.2
  */
-define(['Dictionary', 'EventHandler', 'List'], function(Dictionary, EventHandler, List) {
+define(['Dictionary', 'Dom/ChangeListener', 'EventHandler', 'List', 'Dom/Util'], function(Dictionary, DomChangeListener, EventHandler, List, DomUtil) {
        "use strict";
        
        /**
@@ -33,12 +33,19 @@ define(['Dictionary', 'EventHandler', 'List'], function(Dictionary, EventHandler
        var _dependencyHiddenNodes = new List();
        
        /**
-        * list if fields for which event listeners have been registered
+        * list of fields for which event listeners have been registered
         * @type        {Dictionary}
         * @private
         */
        var _fields = new Dictionary();
        
+       /**
+        * list of registered forms
+        * @type        {List}
+        * @private
+        */
+       var _forms = new List();
+       
        /**
         * list of dependencies grouped by the dependent node they belong to
         * @type        {Dictionary}
@@ -46,7 +53,48 @@ define(['Dictionary', 'EventHandler', 'List'], function(Dictionary, EventHandler
         */
        var _nodeDependencies = new Dictionary();
        
+       /**
+        * list of required fields
+        * @type        {List}
+        * @private
+        */
+       var _validatedFields = new List();
+       
        return {
+               /**
+                * Check if for an invalid form field if it has been hidden due to dependencies
+                * and discards any validation error message if that is the case.
+                * 
+                * @param       {Event}         event   `invalid` form field event 
+                * @protected
+                */
+               _checkRequiredField: function(event) {
+                       _dependencyHiddenNodes.forEach(function(hiddenNode) {
+                               if (DomUtil.contains(hiddenNode, event.currentTarget)) {
+                                       event.preventDefault();
+                                       event.stopPropagation();
+                               };
+                       });
+               },
+               
+               /**
+                * Registers the (new) required fields of all registered forms.
+                * 
+                * @protected
+                */
+               _registerValidatedFields: function() {
+                       _forms.forEach(function(form) {
+                               // `minlength` does not trigger `invalid` events
+                               elBySelAll('[max], [maxlength], [min], [required]', form, function(validatedField) {
+                                       if (!_validatedFields.has(validatedField)) {
+                                               _validatedFields.add(validatedField);
+                                               
+                                               validatedField.addEventListener('invalid', this._checkRequiredField.bind(this));
+                                       }
+                               }.bind(this))
+                       }.bind(this));
+               },
+               
                /**
                 * Hides the given node because of its own dependencies.
                 * 
@@ -55,18 +103,18 @@ define(['Dictionary', 'EventHandler', 'List'], function(Dictionary, EventHandler
                 */
                _hide: function(node) {
                        elHide(node);
-                       _dependencyHiddenNodes.add(node.id);
+                       _dependencyHiddenNodes.add(node);
                },
                
                /**
                 * Shows the given node because of its own dependencies.
-                *
+                * 
                 * @param       {HTMLElement}   node    shown node
                 * @protected
                 */
                _show: function(node) {
                        elShow(node);
-                       _dependencyHiddenNodes.delete(node.id);
+                       _dependencyHiddenNodes.delete(node);
                },
                
                /**
@@ -183,7 +231,34 @@ define(['Dictionary', 'EventHandler', 'List'], function(Dictionary, EventHandler
                 * @return      {boolean}
                 */
                isHiddenByDependencies: function(node) {
-                       return _dependencyHiddenNodes.has(node.id);
+                       return _dependencyHiddenNodes.has(node);
+               },
+               
+               /**
+                * Registers the form with the given id with the dependency manager.
+                * 
+                * @param       {string}        formId          id of register form
+                * @throws      {Error}                         if given form id is invalid or has already been registered
+                */
+               register: function(formId) {
+                       var form = elById(formId);
+                       
+                       if (form === null) {
+                               throw new Error("Unknown element with id '" + formId + "'");
+                       }
+                       if (form.tagName !== 'FORM') {
+                               throw new Error("Element with id '" + formId + "' is no form.");
+                       }
+                       
+                       if (_forms.has(form)) {
+                               throw new Error("Form with id '" + formId + "' has already been registered.");
+                       }
+                       
+                       _forms.add(form);
+                       
+                       this._registerValidatedFields();
+                       
+                       DomChangeListener.add('WoltLabSuite/Core/Form/Builder/Field/Dependency/Manager', this._registerValidatedFields.bind(this));
                }
        };
 });