diff --git a/js/initial-calculation.js b/js/initial-calculation.js index 24d748a..3a9b0df 100644 --- a/js/initial-calculation.js +++ b/js/initial-calculation.js @@ -18,14 +18,22 @@ /** * Performs a calculation when a form is initially loaded. */ -(function (Drupal, once) { +(function (Drupal, once, $) { Drupal.behaviors.json_forms_initial_calculation = { attach: function (context, settings) { once('json-forms-initial-calculation', '[data-json-forms-init-calculation="1"]', context).forEach((element) => { + if (element.disabled) { + // Drupal enables the element after the AJAX call so we have to disable it again. + const handler = function (event) { + element.disabled = true; + $(document).off('ajaxStop', handler); + }; + $(document).on('ajaxStop', handler); + } // Trigger a "change" event which results in an AJAX call that performs the calculations. element.dispatchEvent(new Event('change')); }); } }; -})(Drupal, once); +})(Drupal, once, jQuery); diff --git a/json_forms.libraries.yml b/json_forms.libraries.yml index 695414e..8a269fb 100644 --- a/json_forms.libraries.yml +++ b/json_forms.libraries.yml @@ -17,7 +17,7 @@ disable_buttons_on_ajax: - core/jquery initial_calculation: - version: 0.1.0 + version: 0.1.1 js: js/initial-calculation.js: {} dependencies: diff --git a/src/Form/Control/Callbacks/TriggeringElementCallback.php b/src/Form/Control/Callbacks/TriggeringElementCallback.php new file mode 100644 index 0000000..b198fdd --- /dev/null +++ b/src/Form/Control/Callbacks/TriggeringElementCallback.php @@ -0,0 +1,71 @@ +. + */ + +declare(strict_types=1); + +namespace Drupal\json_forms\Form\Control\Callbacks; + +use Drupal\Core\Form\FormStateInterface; + +final class TriggeringElementCallback { + + /** + * @param array $element + * + * @return array + */ + public static function onAfterBuild(array $element, FormStateInterface $formState): array { + // For disabled elements the triggering element is not set. (Required if + // disabled element is used for the initial calculation call.) + if (NULL === $formState->getTriggeringElement() && self::elementTriggeredScriptedSubmission($element, $formState)) { + $formState->setTriggeringElement($element); + } + + return $element; + } + + /** + * Based on + * \Drupal\Core\Form\FormBuilder::elementTriggeredScriptedSubmission(). + * + * Detects if an element triggered the form submission via Ajax. + * + * This detects button or non-button controls that trigger a form submission + * via Ajax or some other scriptable environment. These environments can set + * the special input key '_triggering_element_name' to identify the triggering + * element. If the name alone doesn't identify the element uniquely, the input + * key '_triggering_element_value' may also be set to require a match on + * element value. An example where this is needed is if there are several + * // buttons all named 'op', and only differing in their value. + * + * @param array $element + */ + private static function elementTriggeredScriptedSubmission(array $element, FormStateInterface $formState): bool { + $input = $formState->getUserInput(); + if ($element['#name'] === ($input['_triggering_element_name'] ?? NULL)) { + // @phpstan-ignore equal.notAllowed + if (!isset($input['_triggering_element_value']) || $input['_triggering_element_value'] == $element['#value']) { + return TRUE; + } + } + + return FALSE; + } + +} diff --git a/src/Form/Control/Util/BasicFormPropertiesFactory.php b/src/Form/Control/Util/BasicFormPropertiesFactory.php index e7d6fc5..8300a25 100644 --- a/src/Form/Control/Util/BasicFormPropertiesFactory.php +++ b/src/Form/Control/Util/BasicFormPropertiesFactory.php @@ -23,6 +23,7 @@ use Drupal\Core\Form\FormStateInterface; use Drupal\json_forms\Form\Control\Callbacks\RecalculateCallback; +use Drupal\json_forms\Form\Control\Callbacks\TriggeringElementCallback; use Drupal\json_forms\Form\Control\Rule\StatesArrayFactory; use Drupal\json_forms\Form\Util\DescriptionDisplayUtil; use Drupal\json_forms\JsonForms\Definition\Control\ControlDefinition; @@ -141,6 +142,11 @@ public static function createFieldProperties(ControlDefinition $definition, Form $form['#process'] = [ [RecalculateCallback::class, 'processElement'], ]; + if ($calcInitField && $form['#disabled']) { + $form['#after_build'] = [ + [TriggeringElementCallback::class, 'onAfterBuild'], + ]; + } } $form += static::createBasicProperties($definition);