From 2900bc96137e97c72ea34494ae5fcd03d70a0f09 Mon Sep 17 00:00:00 2001 From: Allan Alf Nielsen Date: Tue, 13 Jan 2026 12:53:29 +0100 Subject: [PATCH 1/3] [LiveComponent] Fix DataModelPropsSubscriber parsing LiveComponent modifiers as TwigComponent bindings The data-model attribute is used by both TwigComponent (for parent-child prop binding) and LiveComponent (for form input binding with modifiers). This was causing LiveComponent modifiers like 'norender|field' or 'debounce(100)|field' to be incorrectly parsed as TwigComponent bindings. This fix adds modifier detection to DataModelPropsSubscriber that checks if data-model contains LiveComponent modifier syntax (pipe '|' or function calls) and extracts the value from the modifier syntax Fixes #3152, #555 --- .../DataModelPropsSubscriber.php | 6 ++++ .../ParentComponentDataModelWithModifiers.php | 22 ++++++++++++ ...ParentComponentDataModelWithModifiers2.php | 24 +++++++++++++ .../ParentFormComponentWithModifiers.php | 25 +++++++++++++ ...ponent_data_model_with_modifiers.html.twig | 2 ++ ...nent_data_model_with_modifiers_2.html.twig | 2 ++ ...nt_form_component_with_modifiers.html.twig | 11 ++++++ .../DataModelPropsSubscriberTest.php | 35 +++++++++++++++++++ 8 files changed, 127 insertions(+) create mode 100644 src/LiveComponent/tests/Fixtures/Component/ParentComponentDataModelWithModifiers.php create mode 100644 src/LiveComponent/tests/Fixtures/Component/ParentComponentDataModelWithModifiers2.php create mode 100644 src/LiveComponent/tests/Fixtures/Component/ParentFormComponentWithModifiers.php create mode 100644 src/LiveComponent/tests/Fixtures/templates/components/parent_component_data_model_with_modifiers.html.twig create mode 100644 src/LiveComponent/tests/Fixtures/templates/components/parent_component_data_model_with_modifiers_2.html.twig create mode 100644 src/LiveComponent/tests/Fixtures/templates/components/parent_form_component_with_modifiers.html.twig diff --git a/src/LiveComponent/src/EventListener/DataModelPropsSubscriber.php b/src/LiveComponent/src/EventListener/DataModelPropsSubscriber.php index 8cc4ce37523..71b9602e224 100644 --- a/src/LiveComponent/src/EventListener/DataModelPropsSubscriber.php +++ b/src/LiveComponent/src/EventListener/DataModelPropsSubscriber.php @@ -65,6 +65,12 @@ public function onPreMount(PreMountEvent $event): void $childModel = $binding['child']; $parentModel = $binding['parent']; + // If the data-model attribute contains LiveComponent-specific modifiers, extract the actual property name + if (str_contains($parentModel, '|')) { + $parentModelParts = explode('|', $parentModel); + $parentModel = end($parentModelParts); + } + $data[$childModel] = $this->propertyAccessor->getValue($parentMountedComponent->getComponent(), $parentModel); } diff --git a/src/LiveComponent/tests/Fixtures/Component/ParentComponentDataModelWithModifiers.php b/src/LiveComponent/tests/Fixtures/Component/ParentComponentDataModelWithModifiers.php new file mode 100644 index 00000000000..26d091cbf18 --- /dev/null +++ b/src/LiveComponent/tests/Fixtures/Component/ParentComponentDataModelWithModifiers.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\UX\LiveComponent\Tests\Fixtures\Component; + +use Symfony\UX\LiveComponent\Attribute\AsLiveComponent; +use Symfony\UX\LiveComponent\Attribute\LiveProp; +use Symfony\UX\LiveComponent\DefaultActionTrait; + +#[AsLiveComponent('parent_component_data_model_with_modifiers')] +final class ParentComponentDataModelWithModifiers +{ + use DefaultActionTrait; +} diff --git a/src/LiveComponent/tests/Fixtures/Component/ParentComponentDataModelWithModifiers2.php b/src/LiveComponent/tests/Fixtures/Component/ParentComponentDataModelWithModifiers2.php new file mode 100644 index 00000000000..70e88a7780c --- /dev/null +++ b/src/LiveComponent/tests/Fixtures/Component/ParentComponentDataModelWithModifiers2.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\UX\LiveComponent\Tests\Fixtures\Component; + +use Symfony\UX\LiveComponent\Attribute\AsLiveComponent; +use Symfony\UX\LiveComponent\Attribute\LiveProp; +use Symfony\UX\LiveComponent\DefaultActionTrait; + +#[AsLiveComponent('parent_component_data_model_with_modifiers_2')] +final class ParentComponentDataModelWithModifiers2 +{ + use DefaultActionTrait; + + #[LiveProp(writable: true)] public string $content; +} diff --git a/src/LiveComponent/tests/Fixtures/Component/ParentFormComponentWithModifiers.php b/src/LiveComponent/tests/Fixtures/Component/ParentFormComponentWithModifiers.php new file mode 100644 index 00000000000..391f34a230e --- /dev/null +++ b/src/LiveComponent/tests/Fixtures/Component/ParentFormComponentWithModifiers.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\UX\LiveComponent\Tests\Fixtures\Component; + +use Symfony\UX\LiveComponent\Attribute\AsLiveComponent; +use Symfony\UX\LiveComponent\DefaultActionTrait; + +#[AsLiveComponent('parent_form_component_with_modifiers')] +final class ParentFormComponentWithModifiers +{ + use DefaultActionTrait; + + public ?string $content = null; + + public ?string $content2 = null; +} diff --git a/src/LiveComponent/tests/Fixtures/templates/components/parent_component_data_model_with_modifiers.html.twig b/src/LiveComponent/tests/Fixtures/templates/components/parent_component_data_model_with_modifiers.html.twig new file mode 100644 index 00000000000..72982f2baf5 --- /dev/null +++ b/src/LiveComponent/tests/Fixtures/templates/components/parent_component_data_model_with_modifiers.html.twig @@ -0,0 +1,2 @@ +{% component parent_component_data_model_with_modifiers_2 with { content: 'default content on mount' } %} +{% endcomponent %} diff --git a/src/LiveComponent/tests/Fixtures/templates/components/parent_component_data_model_with_modifiers_2.html.twig b/src/LiveComponent/tests/Fixtures/templates/components/parent_component_data_model_with_modifiers_2.html.twig new file mode 100644 index 00000000000..aad832ff513 --- /dev/null +++ b/src/LiveComponent/tests/Fixtures/templates/components/parent_component_data_model_with_modifiers_2.html.twig @@ -0,0 +1,2 @@ +{{ component('textarea_component', { dataModel: 'norender|content' }) }} +{% component input_component with { dataModel: 'norender|content' } %}{% endcomponent %} diff --git a/src/LiveComponent/tests/Fixtures/templates/components/parent_form_component_with_modifiers.html.twig b/src/LiveComponent/tests/Fixtures/templates/components/parent_form_component_with_modifiers.html.twig new file mode 100644 index 00000000000..2d5d4b762de --- /dev/null +++ b/src/LiveComponent/tests/Fixtures/templates/components/parent_form_component_with_modifiers.html.twig @@ -0,0 +1,11 @@ +
+ {{ component('textarea_component', { + 'data-model': 'norender|content:value' + }) }} +
+ +
+ {{ component('textarea_component', { + 'dataModel': 'norender|content2:value' + }) }} +
diff --git a/src/LiveComponent/tests/Integration/EventListener/DataModelPropsSubscriberTest.php b/src/LiveComponent/tests/Integration/EventListener/DataModelPropsSubscriberTest.php index 7bdeb34d0ca..534775781dd 100644 --- a/src/LiveComponent/tests/Integration/EventListener/DataModelPropsSubscriberTest.php +++ b/src/LiveComponent/tests/Integration/EventListener/DataModelPropsSubscriberTest.php @@ -54,4 +54,39 @@ public function testDataModelPropsAreAvailableInEmbeddedComponents() $this->assertStringContainsString('', $html); $this->assertStringContainsString('', $html); } + + public function testDataModelPropsWithModifiersAreSharedToChild() + { + /** @var ComponentRenderer $renderer */ + $renderer = self::getContainer()->get('ux.twig_component.component_renderer'); + + $html = $renderer->createAndRender('parent_form_component_with_modifiers', [ + 'content' => 'Hello data-model!', + 'content2' => 'Value for second child', + 'attributes' => ['id' => 'dummy-live-id'], + ]); + + // Verify that the data-model attributes include the "norender" modifier and that values are passed correctly + $this->assertStringContainsString('', $html); + $this->assertStringContainsString('', $html); + } + + public function testDataModelPropsWithModifiersAreAvailableInEmbeddedComponents() + { + $templateName = 'components/parent_component_data_model_with_modifiers.html.twig'; + $obscuredName = '684c45bf85d3461dbe587407892e59d9'; + $this->addTemplateMap($obscuredName, $templateName); + + /** @var ComponentRenderer $renderer */ + $renderer = self::getContainer()->get('ux.twig_component.component_renderer'); + + $html = $renderer->createAndRender('parent_component_data_model_with_modifiers', [ + 'attributes' => ['id' => 'dummy-live-id'], + ]); + + // Verify that the data-model attributes include the "norender" modifier and that values are passed correctly + $this->assertStringContainsString('', $html); + $this->assertStringContainsString('', $html); + } + } From 9448464d05767ac098bb7c7f0a46d41fe79b2dd2 Mon Sep 17 00:00:00 2001 From: Allan Alf Nielsen Date: Fri, 16 Jan 2026 12:56:49 +0100 Subject: [PATCH 2/3] Apply PHP-CS-fixer fixes --- .../Integration/EventListener/DataModelPropsSubscriberTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/LiveComponent/tests/Integration/EventListener/DataModelPropsSubscriberTest.php b/src/LiveComponent/tests/Integration/EventListener/DataModelPropsSubscriberTest.php index 534775781dd..8a72207af0e 100644 --- a/src/LiveComponent/tests/Integration/EventListener/DataModelPropsSubscriberTest.php +++ b/src/LiveComponent/tests/Integration/EventListener/DataModelPropsSubscriberTest.php @@ -88,5 +88,4 @@ public function testDataModelPropsWithModifiersAreAvailableInEmbeddedComponents( $this->assertStringContainsString('', $html); $this->assertStringContainsString('', $html); } - } From 773d284a0ee772bf14d8e74a7e39ecf7ca0c69c1 Mon Sep 17 00:00:00 2001 From: "Allan A. Nielsen" Date: Mon, 19 Jan 2026 21:35:48 +0100 Subject: [PATCH 3/3] Fix codestyle for LiveProp attribute MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Simon André --- .../Component/ParentComponentDataModelWithModifiers2.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/LiveComponent/tests/Fixtures/Component/ParentComponentDataModelWithModifiers2.php b/src/LiveComponent/tests/Fixtures/Component/ParentComponentDataModelWithModifiers2.php index 70e88a7780c..4fd598c6e0c 100644 --- a/src/LiveComponent/tests/Fixtures/Component/ParentComponentDataModelWithModifiers2.php +++ b/src/LiveComponent/tests/Fixtures/Component/ParentComponentDataModelWithModifiers2.php @@ -20,5 +20,6 @@ final class ParentComponentDataModelWithModifiers2 { use DefaultActionTrait; - #[LiveProp(writable: true)] public string $content; + #[LiveProp(writable: true)] + public string $content; }