Skip to content

Commit 2ac924c

Browse files
committed
Clearing error on dependent field
1 parent f6a13b2 commit 2ac924c

File tree

1 file changed

+55
-2
lines changed

1 file changed

+55
-2
lines changed

src/DynamicFormBuilder.php

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,13 @@
1111

1212
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
1313
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
14+
use Symfony\Component\Form\ClearableErrorsInterface;
1415
use Symfony\Component\Form\DataMapperInterface;
1516
use Symfony\Component\Form\DataTransformerInterface;
17+
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
1618
use Symfony\Component\Form\FormBuilderInterface;
1719
use Symfony\Component\Form\FormConfigInterface;
20+
use Symfony\Component\Form\FormError;
1821
use Symfony\Component\Form\FormEvent;
1922
use Symfony\Component\Form\FormEvents;
2023
use Symfony\Component\Form\FormFactoryInterface;
@@ -49,11 +52,29 @@ public function __construct(private FormBuilderInterface $builder)
4952
$this->form = $event->getForm();
5053
$this->preSetDataDependencyData = [];
5154
$this->initializeListeners();
55+
56+
// A fake hidden field where we can "store" an error if a dependent form
57+
// field is suddenly invalid because its previous data was invalid
58+
// and a field it depends on just changed (e.g. user selected "Michigan"
59+
// as a state, then the user changed "Country" from "USA" to "Mexico"
60+
// and so now "Michigan" is invalid). In this case, we clear the error
61+
// on the actual field, but store a "fake" error here, which won't be
62+
// rendered, but will prevent the form from being valid.
63+
if (!$this->form->has('__dynamic_error')) {
64+
$this->form->add('__dynamic_error', HiddenType::class, [
65+
'mapped' => false,
66+
'error_bubbling' => false,
67+
]);
68+
}
5269
}, 100);
5370

54-
$builder->addEventListener(FormEvents::POST_SUBMIT, function () {
71+
$builder->addEventListener(FormEvents::POST_SUBMIT, function (FormEvent $event) {
5572
$this->postSubmitDependencyData = [];
5673
});
74+
// guarantee later than core ValidationListener
75+
$builder->addEventListener(FormEvents::POST_SUBMIT, function (FormEvent $event) {
76+
$this->clearDataOnTransformationError($event);
77+
}, -1);
5778
}
5879

5980
public function addDependent(string $name, array $dependencies, callable $callback): self
@@ -79,6 +100,34 @@ public function storePostSubmitDependencyData(FormEvent $event): void
79100
$this->executeReadyCallbacks($this->postSubmitDependencyData, FormEvents::POST_SUBMIT);
80101
}
81102

103+
public function clearDataOnTransformationError(FormEvent $event): void
104+
{
105+
$form = $event->getForm();
106+
$transformationErrorsCleared = false;
107+
foreach ($this->dependentFieldConfigs as $dependentFieldConfig) {
108+
if (!$form->has($dependentFieldConfig->name)) {
109+
continue;
110+
}
111+
112+
$subForm = $form->get($dependentFieldConfig->name);
113+
if ($subForm->getTransformationFailure() && $subForm instanceof ClearableErrorsInterface) {
114+
$subForm->clearErrors();
115+
$transformationErrorsCleared = true;
116+
}
117+
}
118+
119+
if ($transformationErrorsCleared) {
120+
// We've cleared the error, but the bad data remains on the field.
121+
// We need to make sure that the form doesn't submit successfully,
122+
// but we also don't want to render a validation error on any field.
123+
// So, we jam the error into a hidden field, which doesn't render errors.
124+
if ($form->get('__dynamic_error')->isValid()) {
125+
$form->get('__dynamic_error')->addError(new FormError('Some dynamic fields have errors.'));
126+
}
127+
dump($form);
128+
}
129+
}
130+
82131
private function executeReadyCallbacks(array $availableDependencyData, string $eventName): void
83132
{
84133
foreach ($this->dependentFieldConfigs as $dependentFieldConfig) {
@@ -129,7 +178,11 @@ private function initializeListeners(array $fieldsToConsider = null): void
129178
}
130179

131180
/*
132-
* Pure method declarations below
181+
* ----------------------------------------
182+
*
183+
* Pure decoration methods below.
184+
*
185+
* ----------------------------------------
133186
*/
134187

135188
public function count(): int

0 commit comments

Comments
 (0)