diff --git a/js/forms/forms.js b/js/forms/forms.js new file mode 100644 index 000000000..92334671b --- /dev/null +++ b/js/forms/forms.js @@ -0,0 +1,14 @@ +/* + * @copyright Copyright (C) 2010-2025 Combodo SARL + * @license http://opensource.org/licenses/AGPL-3.0 + */ + + + +function triggerTurbo(el) { + let sFormName = el.form.getAttribute("name"); + el.form.querySelector(`[name="${sFormName}[_turbo_trigger]"]`).value = el.getAttribute('name'); + el.form.setAttribute('novalidate', true); + el.form.requestSubmit(); + console.log('Auto submitting form due to change in field ' + el.getAttribute('name')); +} \ No newline at end of file diff --git a/sources/Application/WebPage/iTopWebPage.php b/sources/Application/WebPage/iTopWebPage.php index 5692ff562..48cd9e574 100644 --- a/sources/Application/WebPage/iTopWebPage.php +++ b/sources/Application/WebPage/iTopWebPage.php @@ -164,6 +164,8 @@ class iTopWebPage extends NiceWebPage implements iTabbedPage { parent::InitializeLinkedScripts(); + $this->LinkScriptFromAppRoot('js/forms/forms.js'); + // Used by forms $this->LinkScriptFromAppRoot('js/leave_handler.js'); diff --git a/sources/Forms/Block/AbstractFormBlock.php b/sources/Forms/Block/AbstractFormBlock.php index 28b06f7bd..87e84a74b 100644 --- a/sources/Forms/Block/AbstractFormBlock.php +++ b/sources/Forms/Block/AbstractFormBlock.php @@ -13,8 +13,6 @@ use Combodo\iTop\Forms\Block\IO\Format\RawFormat; use Combodo\iTop\Forms\Block\IO\FormInput; use Combodo\iTop\Forms\Block\IO\FormOutput; use Combodo\iTop\Forms\IFormBlock; -use IssueLog; -use Symfony\Component\Filesystem\Exception\IOException; /** * Abstract form block. @@ -104,9 +102,9 @@ abstract class AbstractFormBlock implements IFormBlock /** * Get the parent block. * - * @return FormBlock + * @return FormBlock|null */ - public function GetParent(): FormBlock + public function GetParent(): ?FormBlock { return $this->oParent; } @@ -121,6 +119,15 @@ abstract class AbstractFormBlock implements IFormBlock return $this->sName; } + public function GetIdentifier(): string + { + $sParentName = $this->GetParent()?->GetIdentifier(); + if (is_null($sParentName)) { + return $this->GetName(); + } + return $sParentName.'_'.$this->sName; + } + /** * Return the form block options. * Options will be passed to FormType for building. diff --git a/sources/Forms/Block/Base/FormBlock.php b/sources/Forms/Block/Base/FormBlock.php index 5774cbea2..b2b6af198 100644 --- a/sources/Forms/Block/Base/FormBlock.php +++ b/sources/Forms/Block/Base/FormBlock.php @@ -9,6 +9,7 @@ namespace Combodo\iTop\Forms\Block\Base; use Combodo\iTop\Forms\Block\AbstractFormBlock; use Combodo\iTop\Forms\Block\FormBlockException; use Combodo\iTop\Forms\Block\FormType\FormType; +use Combodo\iTop\Forms\FormBuilder\DependencyMap; use Combodo\iTop\Forms\FormsException; use Exception; use ReflectionClass; @@ -19,8 +20,9 @@ use ReflectionClass; */ class FormBlock extends AbstractFormBlock { - /** @var array children blocks */ + /** @var AbstractFormBlock[] children blocks */ private array $aChildrenBlocks = []; + public ?DependencyMap $oDependencyMap = null; /** * Constructor. @@ -56,7 +58,7 @@ class FormBlock extends AbstractFormBlock $aUserOptions['compound'] = true; $aUserOptions['attr'] = [ - 'class' => 'form' + 'class' => 'form', ]; } @@ -118,4 +120,20 @@ class FormBlock extends AbstractFormBlock } + public function GetSubFormBlock(string $sBlockTurboTriggerName): ?AbstractFormBlock + { + $oBlock = $this; + if (preg_match_all('/\[(?[^\[]+)\]/', $sBlockTurboTriggerName, $aMatches)) { + foreach ($aMatches['level'] as $level) { + $oBlock = $oBlock->Get($level); + } + } + + return $oBlock; + } + + public function GetDependenciesMap(): ?DependencyMap + { + return $this->oDependencyMap; + } } \ No newline at end of file diff --git a/sources/Forms/FormBuilder/DependencyMap.php b/sources/Forms/FormBuilder/DependencyMap.php index 99ae12195..ff0645a11 100644 --- a/sources/Forms/FormBuilder/DependencyMap.php +++ b/sources/Forms/FormBuilder/DependencyMap.php @@ -161,8 +161,8 @@ class DependencyMap public function GetImpacted(string $sBlockName): array { $aImpacted = []; - if (array_key_exists($sBlockName, $this->aOutputToInputsMap)) { - foreach ($this->aOutputToInputsMap[$sBlockName] as $aBindings) { + if (array_key_exists($sBlockName, $this->aBindingsOutputToInput)) { + foreach ($this->aBindingsOutputToInput[$sBlockName] as $aBindings) { foreach ($aBindings as $oBinding) { $oDestBlock = $oBinding->oDestinationIO->GetOwnerBlock(); $aImpacted[] = $oDestBlock; diff --git a/sources/Forms/FormBuilder/FormBuilder.php b/sources/Forms/FormBuilder/FormBuilder.php index 5b852e0eb..9d8015f50 100644 --- a/sources/Forms/FormBuilder/FormBuilder.php +++ b/sources/Forms/FormBuilder/FormBuilder.php @@ -13,6 +13,7 @@ use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\Form\DataMapperInterface; use Symfony\Component\Form\DataTransformerInterface; +use Symfony\Component\Form\Extension\Core\Type\HiddenType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormConfigInterface; use Symfony\Component\Form\FormFactoryInterface; @@ -87,6 +88,11 @@ class FormBuilder implements FormBuilderInterface, IteratorAggregate // Create a dependency handler if needed if (count($aBlocksWithDependencies) > 0) { $this->oDependencyHandler = new DependencyHandler($this->builder->getName(), $oFormBlock, $this, $this->aChildren, $aBlocksWithDependencies); + $oFormBlock->oDependencyMap = $this->oDependencyHandler->GetMap(); + if (is_null($oFormBlock->GetParent())) { + // Insert a hidden type to save the place + $this->builder->add('_turbo_trigger', HiddenType::class, ['prevent_form_build' => true]); + } } } @@ -101,26 +107,15 @@ class FormBuilder implements FormBuilderInterface, IteratorAggregate { // Has dependencies blocks - if ($oSubFormBlock->HasDependenciesBlocks()) { - -// // Insert a hidden type to save the place -// $this->builder->add($oSubFormBlock->GetName(), HiddenType::class, [ -// 'form_block' => $oSubFormBlock, -// 'prevent_form_build' => true, - //// 'mapped' => false, - //// 'disabled' => true, -// ]); - - return true; - - } else { + if (!$oSubFormBlock->HasDependenciesBlocks()) { // Directly insert the block corresponding form type $this->add($oSubFormBlock->GetName(), $oSubFormBlock->GetFormType(), $oSubFormBlock->GetOptions()); $oSubFormBlock->SetAdded(true); return false; - } + + return true; } /** diff --git a/templates/application/forms/itop_console_layout.html.twig b/templates/application/forms/itop_console_layout.html.twig index 1c435afbe..75449723a 100644 --- a/templates/application/forms/itop_console_layout.html.twig +++ b/templates/application/forms/itop_console_layout.html.twig @@ -5,7 +5,7 @@ {%- block widget_attributes -%} {{- parent() -}} {% if trigger_form_submit_on_modify %} - onChange="this.form.setAttribute('novalidate', true); this.form.requestSubmit();console.log('Auto submitting form due to change in field {{ full_name }}');" + onChange="triggerTurbo(this);" {% endif %} {%- endblock widget_attributes -%} @@ -48,7 +48,7 @@ {%- block form_rows -%} {% for block in blocks %} -
+
{{ block.name }} {{ block.added }} {% if block.added == 1 %} {{ form_row(form[block.name]) }}