Skip to content

Commit f46a52a

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

File tree

4 files changed

+88
-3
lines changed

4 files changed

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

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)