Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions js/initial-calculation.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
2 changes: 1 addition & 1 deletion json_forms.libraries.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
71 changes: 71 additions & 0 deletions src/Form/Control/Callbacks/TriggeringElementCallback.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<?php

/*
* Copyright (C) 2025 SYSTOPIA GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

declare(strict_types=1);

namespace Drupal\json_forms\Form\Control\Callbacks;

use Drupal\Core\Form\FormStateInterface;

final class TriggeringElementCallback {

/**
* @param array<int|string, mixed> $element
*
* @return array<int|string, mixed>
*/
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<int|string, mixed> $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;
}

}
6 changes: 6 additions & 0 deletions src/Form/Control/Util/BasicFormPropertiesFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down