Skip to content

[LiveComponent] Smarter form trait data extracting #866

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/LiveComponent/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@
"symfony/dependency-injection": "^5.4|^6.0",
"symfony/form": "^5.4|^6.0",
"symfony/framework-bundle": "^5.4|^6.0",
"symfony/property-info": "^5.4|^6.0",
"symfony/phpunit-bridge": "^6.0",
"symfony/property-info": "^5.4|^6.0",
"symfony/security-csrf": "^5.4|^6.0",
"symfony/twig-bundle": "^5.4|^6.0",
"symfony/validator": "^5.4|^6.0",
Expand Down
12 changes: 7 additions & 5 deletions src/LiveComponent/src/ComponentWithFormTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ public function initializeForm(array $data): array
}

// set the formValues from the initial form view's data
$this->formValues = $this->extractFormValues($this->getForm());
$this->formValues = $this->extractFormValues($this->getForm(), $this->getFormInstance());

return $data;
}
Expand Down Expand Up @@ -157,7 +157,7 @@ private function submitForm(bool $validateAll = true): void

// re-extract the "view" values in case the submitted data
// changed the underlying data or structure of the form
$this->formValues = $this->extractFormValues($this->getForm());
$this->formValues = $this->extractFormValues($this->getForm(), $form);

// remove any validatedFields that do not exist in data anymore
$this->validatedFields = LiveFormUtility::removePathsNotInData(
Expand Down Expand Up @@ -223,7 +223,7 @@ private function getDataModelValue(): ?string
* frontend, and it's meant to equal the raw POST data that would
* be sent if the form were submitted without modification.
*/
private function extractFormValues(FormView $formView): array
private function extractFormValues(FormView $formView, FormInterface $form): array
{
$values = [];

Expand All @@ -235,8 +235,10 @@ private function extractFormValues(FormView $formView): array
// is already correct. For example, an expanded ChoiceType with
// options "text" and "phone" would already have a value in the format
// ["text"] (assuming "text" is checked and "phone" is not).
if (!($child->vars['expanded'] ?? false) && ($child->vars['compound'] ?? false)) {
$values[$name] = $this->extractFormValues($child);
//
$isCompound = $form->has($name) && $form->get($name)->getConfig()->getOption('compound', false);
if ($isCompound && !($child->vars['expanded'] ?? false)) {
$values[$name] = $this->extractFormValues($child, $form->get($name));

continue;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
#[AsLiveComponent('form_with_collection_type')]
class FormWithCollectionTypeComponent extends AbstractController
{
public bool $enableCsrf = false;

use ComponentWithFormTrait;
use DefaultActionTrait;

Expand All @@ -39,7 +41,9 @@ public function __construct()

protected function instantiateForm(): FormInterface
{
return $this->createForm(BlogPostFormType::class, $this->post);
return $this->createForm(BlogPostFormType::class, $this->post, [
'csrf_protection' => $this->enableCsrf,
]);
}

#[LiveAction]
Expand Down
2 changes: 1 addition & 1 deletion src/LiveComponent/tests/Fixtures/Form/BlogPostFormType.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ public function buildForm(FormBuilderInterface $builder, array $options)
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'csrf_protection' => false,
'data_class' => BlogPost::class,
'csrf_protection' => false,
]);
}
}
24 changes: 24 additions & 0 deletions src/LiveComponent/tests/Fixtures/Form/ComplexFieldType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

namespace Symfony\UX\LiveComponent\Tests\Fixtures\Form;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormView;
use Symfony\Component\OptionsResolver\OptionsResolver;

class ComplexFieldType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('sub_field', TextType::class);
}

public function finishView(FormView $view, FormInterface $form, array $options)
{
// try to confuse ComponentWithFormTrait
$view->vars['compound'] = false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ public function buildForm(FormBuilderInterface $builder, array $options)
->add('checkbox_checked', CheckboxType::class)
->add('file', FileType::class)
->add('hidden', HiddenType::class)
->add('complexType', ComplexFieldType::class)
;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
<div {{ attributes }}>
{{ form(this.form) }}
{{ form_start(this.form) }}
{#
This is a compound field, but to be tricky, we change its compound
view value to false. This causes the rendering system (correctly) to
choke on it. So, we render it manually. This imitates the behavior
of the UX autocomplete form type.
#}
{{ form_row(this.form.complexType.sub_field) }}
{{ form_widget(this.form) }}
{{ form_end(this.form) }}
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{{ component('form_with_collection_type', {
enableCsrf: true,
}) }}
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,13 @@ class ComponentWithFormTest extends KernelTestCase

public function testFormValuesRebuildAfterFormChanges(): void
{
$dehydratedProps = $this->dehydrateComponent($this->mountComponent('form_with_collection_type'))->getProps();

$browser = $this->browser();
$crawler = $browser
->get('/_components/form_with_collection_type?props='.urlencode(json_encode($dehydratedProps)))
->get('/render-template/render_form_with_collection_type')
->crawler()
;
$div = $crawler->filter('[data-controller="live"]');
$dehydratedProps = json_decode($div->attr('data-live-props-value'), true);

// mimic user typing
$updatedProps = [
Expand Down Expand Up @@ -181,6 +181,9 @@ public function testHandleCheckboxChanges(): void
'checkbox_checked' => '1',
'file' => '',
'hidden' => '',
'complexType' => [
'sub_field' => '',
],
], $dehydratedProps['form']);

$getUrl = function (array $props, array $updatedProps = null) {
Expand Down
3 changes: 3 additions & 0 deletions src/LiveComponent/tests/Unit/Form/ComponentWithFormTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ public function testFormValues(): void
'checkbox_checked' => '1',
'file' => '',
'hidden' => '',
'complexType' => [
'sub_field' => '',
],
],
$component->formValues
);
Expand Down