Skip to content

Commit 5a7ebd4

Browse files
author
Dominic Tubach
committed
Initial calculation with disabled element
1 parent 4fdebeb commit 5a7ebd4

File tree

4 files changed

+87
-3
lines changed

4 files changed

+87
-3
lines changed

js/initial-calculation.js

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,22 @@
1818
/**
1919
* Performs a calculation when a form is initially loaded.
2020
*/
21-
(function (Drupal, once) {
21+
(function (Drupal, once, $) {
2222
Drupal.behaviors.json_forms_initial_calculation = {
2323
attach: function (context, settings) {
2424
once('json-forms-initial-calculation', '[data-json-forms-init-calculation="1"]', context).forEach((element) => {
25+
if (element.disabled) {
26+
// Drupal enables the element after the AJAX call so we have to disable it again.
27+
const handler = function (event) {
28+
element.disabled = true;
29+
$(document).off('ajaxStop', handler);
30+
};
31+
$(document).on('ajaxStop', handler);
32+
}
2533
// Trigger a "change" event which results in an AJAX call that performs the calculations.
2634
element.dispatchEvent(new Event('change'));
2735
});
2836
}
2937
};
3038

31-
})(Drupal, once);
39+
})(Drupal, once, jQuery);

json_forms.libraries.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ disable_buttons_on_ajax:
1717
- core/jquery
1818

1919
initial_calculation:
20-
version: 0.1.0
20+
version: 0.1.1
2121
js:
2222
js/initial-calculation.js: {}
2323
dependencies:
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
<?php
2+
/*
3+
* Copyright (C) 2025 SYSTOPIA GmbH
4+
*
5+
* This program is free software: you can redistribute it and/or modify
6+
* it under the terms of the GNU General Public License as published by
7+
* the Free Software Foundation, either version 2 of the License, or
8+
* (at your option) any later version.
9+
*
10+
* This program is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
* GNU General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU General Public License
16+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
17+
*/
18+
19+
declare(strict_types = 1);
20+
21+
namespace Drupal\json_forms\Form\Control\Callbacks;
22+
23+
use Drupal\Core\Form\FormStateInterface;
24+
25+
final class TriggeringElementCallback {
26+
27+
/**
28+
* @param array<int|string, mixed> $element
29+
*
30+
* @return array<int|string, mixed>
31+
*/
32+
public static function onAfterBuild(array $element, FormStateInterface $formState): array {
33+
// For disabled elements the triggering element is not set. (Required if
34+
// disabled element is used for the initial calculation call.)
35+
if (NULL === $formState->getTriggeringElement() && self::elementTriggeredScriptedSubmission($element, $formState)) {
36+
$formState->setTriggeringElement($element);
37+
}
38+
39+
return $element;
40+
}
41+
42+
/**
43+
* Based on
44+
* \Drupal\Core\Form\FormBuilder::elementTriggeredScriptedSubmission().
45+
*
46+
* Detects if an element triggered the form submission via Ajax.
47+
*
48+
* This detects button or non-button controls that trigger a form submission
49+
* via Ajax or some other scriptable environment. These environments can set
50+
* the special input key '_triggering_element_name' to identify the triggering
51+
* element. If the name alone doesn't identify the element uniquely, the input
52+
* key '_triggering_element_value' may also be set to require a match on
53+
* element value. An example where this is needed is if there are several
54+
* // buttons all named 'op', and only differing in their value.
55+
*
56+
* @param array<int|string, mixed> $element
57+
*/
58+
private static function elementTriggeredScriptedSubmission(array $element, FormStateInterface $formState): bool {
59+
$input = $formState->getUserInput();
60+
if ($element['#name'] === ($input['_triggering_element_name'] ?? NULL)) {
61+
// @phpstan-ignore equal.notAllowed
62+
if (!isset($input['_triggering_element_value']) || $input['_triggering_element_value'] == $element['#value']) {
63+
return TRUE;
64+
}
65+
}
66+
67+
return FALSE;
68+
}
69+
70+
}

src/Form/Control/Util/BasicFormPropertiesFactory.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323

2424
use Drupal\Core\Form\FormStateInterface;
2525
use Drupal\json_forms\Form\Control\Callbacks\RecalculateCallback;
26+
use Drupal\json_forms\Form\Control\Callbacks\TriggeringElementCallback;
2627
use Drupal\json_forms\Form\Control\Rule\StatesArrayFactory;
2728
use Drupal\json_forms\Form\Util\DescriptionDisplayUtil;
2829
use Drupal\json_forms\JsonForms\Definition\Control\ControlDefinition;
@@ -138,6 +139,11 @@ public static function createFieldProperties(ControlDefinition $definition, Form
138139
'progress' => [],
139140
'disable-refocus' => TRUE,
140141
];
142+
if ($calcInitField && $form['#disabled']) {
143+
$form['#after_build'] = [
144+
[TriggeringElementCallback::class, 'onAfterBuild'],
145+
];
146+
}
141147
}
142148

143149
$form += static::createBasicProperties($definition);

0 commit comments

Comments
 (0)