diff --git a/src/TwigComponent/CHANGELOG.md b/src/TwigComponent/CHANGELOG.md index 716e0c555a8..fe925b51b1f 100644 --- a/src/TwigComponent/CHANGELOG.md +++ b/src/TwigComponent/CHANGELOG.md @@ -1,5 +1,9 @@ # CHANGELOG +## Unreleased + +- Add `attributes()` twig function. + ## 2.13.0 - [BC BREAK] Add component metadata to `PreMountEvent` and `PostMountEvent` diff --git a/src/TwigComponent/src/ComponentRenderer.php b/src/TwigComponent/src/ComponentRenderer.php index d45e95c61ff..80ecab36ce6 100644 --- a/src/TwigComponent/src/ComponentRenderer.php +++ b/src/TwigComponent/src/ComponentRenderer.php @@ -110,14 +110,28 @@ public function finishEmbeddedComponentRender(): void $this->dispatcher->dispatch($event); } - private function preRender(MountedComponent $mounted, array $context = []): PreRenderEvent + public function createAttributes(array $attributes = []): ComponentAttributes { - if (!$this->safeClassesRegistered) { - $this->twig->getExtension(EscaperExtension::class)->addSafeClass(ComponentAttributes::class, ['html']); + $this->registerSafeClasses(); + + return new ComponentAttributes($attributes); + } - $this->safeClassesRegistered = true; + private function registerSafeClasses(): void + { + if ($this->safeClassesRegistered) { + return; } + $this->twig->getExtension(EscaperExtension::class)->addSafeClass(ComponentAttributes::class, ['html']); + + $this->safeClassesRegistered = true; + } + + private function preRender(MountedComponent $mounted, array $context = []): PreRenderEvent + { + $this->registerSafeClasses(); + $component = $mounted->getComponent(); $metadata = $this->factory->metadataFor($mounted->getName()); // expose public properties and properties marked with ExposeInTemplate attribute diff --git a/src/TwigComponent/src/Twig/ComponentExtension.php b/src/TwigComponent/src/Twig/ComponentExtension.php index 1df860e96f4..852b8931072 100644 --- a/src/TwigComponent/src/Twig/ComponentExtension.php +++ b/src/TwigComponent/src/Twig/ComponentExtension.php @@ -13,6 +13,7 @@ use Psr\Container\ContainerInterface; use Symfony\Contracts\Service\ServiceSubscriberInterface; +use Symfony\UX\TwigComponent\ComponentAttributes; use Symfony\UX\TwigComponent\ComponentRenderer; use Symfony\UX\TwigComponent\Event\PreRenderEvent; use Twig\Error\RuntimeError; @@ -41,6 +42,7 @@ public function getFunctions(): array { return [ new TwigFunction('component', [$this, 'render'], ['is_safe' => ['all']]), + new TwigFunction('attributes', [$this, 'attributes']), ]; } @@ -61,6 +63,11 @@ public function render(string $name, array $props = []): string } } + public function attributes(array $attributes = []): ComponentAttributes + { + return $this->container->get(ComponentRenderer::class)->createAttributes($attributes); + } + public function extensionPreCreateForRender(string $name, array $props): ?string { try { diff --git a/src/TwigComponent/tests/Integration/ComponentExtensionTest.php b/src/TwigComponent/tests/Integration/ComponentExtensionTest.php index a65c8f3eabe..95b5da4339e 100644 --- a/src/TwigComponent/tests/Integration/ComponentExtensionTest.php +++ b/src/TwigComponent/tests/Integration/ComponentExtensionTest.php @@ -216,6 +216,16 @@ public function testComponentPropsWithTrailingComma(): void $this->assertStringContainsString('Hello FOO, 123, and 456', $output); } + public function testAttributesFunction(): void + { + $output = self::getContainer()->get(Environment::class) + ->createTemplate('
') + ->render() + ; + + $this->assertSame('', $output); + } + private function renderComponent(string $name, array $data = []): string { return self::getContainer()->get(Environment::class)->render('render_component.html.twig', [