Skip to content

Commit 2e4a2fa

Browse files
committed
Adding refreshForm() to ComponentWithFormTrait
This also cleans up how initial form values are calculated
1 parent db75d11 commit 2e4a2fa

File tree

9 files changed

+297
-58
lines changed

9 files changed

+297
-58
lines changed

src/LiveComponent/CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,16 @@
22

33
## 2.1.0
44

5+
- `ComponentWithFromTrait`: Added `refreshForm()` to rebuild form based
6+
on chnaged data
7+
8+
- `ComponentWithFormTrait`: call `$this->initializeFormValues()` from
9+
`mount()` if your component has a custom `mount()`.
10+
11+
- `ComponentWithFormTrait` no longer has a `setForm()` method, just call
12+
`$this->formView = $form` from `mount()` if you have a custom mount.
13+
Hopefully that will not be needed soon.
14+
515
- The Live Component AJAX endpoints now return HTML in all situations
616
instead of JSON.
717

src/LiveComponent/src/ComponentWithFormTrait.php

Lines changed: 39 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,11 @@ trait ComponentWithFormTrait
5858
#[LiveProp(writable: true)]
5959
public array $validatedFields = [];
6060

61+
/**
62+
* Internal flag to track if the form should be submitted on render.
63+
*/
64+
private bool $didFormSubmit = false;
65+
6166
/**
6267
* Return the full, top-level, Form object that this component uses.
6368
*/
@@ -66,14 +71,20 @@ abstract protected function instantiateForm(): FormInterface;
6671
/**
6772
* Override in your class if you need extra mounted values.
6873
*
69-
* Call $this->setForm($form) manually in that situation
70-
* if you're passing in an initial form.
74+
* If you override, call $this->initializeFormValues() manually
75+
* and also $this->formView = $form if you are passing in a Form.
76+
*
77+
* TODO: Refactor to PostMount when available.
78+
* https://github.com/symfony/ux/pull/220
7179
*/
72-
public function mount(?FormView $form = null)
80+
public function mount(FormView $form = null)
7381
{
7482
if ($form) {
75-
$this->setForm($form);
83+
$this->formView = $form;
7684
}
85+
86+
// set the formValues from the initial form view's data
87+
$this->initializeFormValues();
7788
}
7889

7990
/**
@@ -86,7 +97,7 @@ public function mount(?FormView $form = null)
8697
#[BeforeReRender]
8798
public function submitFormOnRender(): void
8899
{
89-
if (!$this->getFormInstance()->isSubmitted()) {
100+
if (!$this->getFormInstance()->isSubmitted() && !$this->didFormSubmit) {
90101
$this->submitForm(false);
91102
}
92103
}
@@ -103,18 +114,6 @@ public function getForm(): FormView
103114
return $this->formView;
104115
}
105116

106-
/**
107-
* Call this from mount() if your component receives a FormView.
108-
*
109-
* If your are not passing a FormView into your component, you
110-
* don't need to call this directly: the form will be set for
111-
* you from your instantiateForm() method.
112-
*/
113-
public function setForm(FormView $form): void
114-
{
115-
$this->formView = $form;
116-
}
117-
118117
public function getFormName(): string
119118
{
120119
if (!$this->formName) {
@@ -124,18 +123,16 @@ public function getFormName(): string
124123
return $this->formName;
125124
}
126125

127-
public function getFormValues(): array
126+
private function initializeFormValues(): void
128127
{
129-
if (null === $this->formValues) {
130-
$this->formValues = $this->extractFormValues($this->getForm());
131-
}
132-
133-
return $this->formValues;
128+
$this->formValues = $this->extractFormValues($this->getForm());
134129
}
135130

136-
private function submitForm(bool $validateAll = true): void
131+
private function submitForm(bool $validateAll = true, bool $throwOnValidationError = true): void
137132
{
138-
$this->getFormInstance()->submit($this->formValues);
133+
$form = $this->getFormInstance();
134+
$form->submit($this->formValues);
135+
$this->didFormSubmit = true;
139136

140137
if ($validateAll) {
141138
// mark the entire component as validated
@@ -146,14 +143,29 @@ private function submitForm(bool $validateAll = true): void
146143
// we only want to validate fields in validatedFields
147144
// but really, everything is validated at this point, which
148145
// means we need to clear validation on non-matching fields
149-
$this->clearErrorsForNonValidatedFields($this->getFormInstance(), $this->getFormName());
146+
$this->clearErrorsForNonValidatedFields($form, $this->getFormName());
150147
}
151148

152-
if (!$this->getFormInstance()->isValid()) {
149+
if ($throwOnValidationError && !$form->isValid()) {
153150
throw new UnprocessableEntityHttpException('Form validation failed in component');
154151
}
155152
}
156153

154+
/**
155+
* Call this to rebuild the form objects.
156+
*
157+
* This is useful in action where you call $this->submitForm(),
158+
* then, after, change some of the form's underlying data. By
159+
* call this function, the form can rebuild using the new data.
160+
*/
161+
private function refreshForm(): void
162+
{
163+
$this->formInstance = null;
164+
$this->formView = null;
165+
// re-initialize the form object and the formValues from it
166+
$this->initializeFormValues();
167+
}
168+
157169
/**
158170
* Returns a hierarchical array of the entire form's values.
159171
*

0 commit comments

Comments
 (0)