Skip to content

Commit b35807b

Browse files
pierredupkbond
authored andcommitted
[LiveComponent] Add 'live_action' twig function
1 parent 03a85b5 commit b35807b

File tree

8 files changed

+94
-0
lines changed

8 files changed

+94
-0
lines changed

src/LiveComponent/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# CHANGELOG
22

33
- Add `submitForm()` to `TestLiveComponent`.
4+
- Add `live_action` Twig function
45

56
## 2.18.0
67

src/LiveComponent/composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
"require": {
2929
"php": ">=8.1",
3030
"symfony/property-access": "^5.4.5|^6.0|^7.0",
31+
"symfony/stimulus-bundle": "^2.9",
3132
"symfony/ux-twig-component": "^2.8",
3233
"twig/twig": "^3.8.0"
3334
},

src/LiveComponent/doc/index.rst

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1106,6 +1106,17 @@ You can also add several "modifiers" to the action:
11061106
The ``debounce(300)`` adds 300ms of "debouncing" before the action is executed.
11071107
In other words, if you click really fast 5 times, only one Ajax request will be made!
11081108

1109+
You can also use the ``live_action`` twig helper function to render the attributes:
1110+
1111+
.. code-block:: html+twig
1112+
1113+
<button {{ live_action('resetMax') }}>Reset Min/Max</button>
1114+
1115+
{# with modifiers #}
1116+
1117+
<button {{ live_action('save', {}, {'debounce': 300}) }}>Save</button>
1118+
1119+
11091120
Actions & Services
11101121
~~~~~~~~~~~~~~~~~~
11111122

@@ -1159,6 +1170,12 @@ You can also pass arguments to your action by adding each as a
11591170
>Add Item</button>
11601171
</form>
11611172

1173+
{# or #}
1174+
1175+
<form>
1176+
<button {{ live_action('addItem', {'id': item.id, 'itemName': 'CustomItem' })>Add Item</button>
1177+
</form>
1178+
11621179
In your component, to allow each argument to be passed, add
11631180
the ``#[LiveArg()]`` attribute::
11641181

src/LiveComponent/src/DependencyInjection/LiveComponentExtension.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,7 @@ function (ChildDefinition $definition, AsLiveComponent $attribute) {
175175
new Reference('ux.twig_component.component_factory'),
176176
new Reference('router'),
177177
new Reference('ux.live_component.metadata_factory'),
178+
new Reference('stimulus.helper'),
178179
])
179180
->addTag('twig.runtime')
180181
;

src/LiveComponent/src/Twig/LiveComponentExtension.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ public function getFunctions(): array
2525
{
2626
return [
2727
new TwigFunction('component_url', [LiveComponentRuntime::class, 'getComponentUrl']),
28+
new TwigFunction('live_action', [LiveComponentRuntime::class, 'liveAction'], ['is_safe' => ['html_attr']]),
2829
];
2930
}
3031
}

src/LiveComponent/src/Twig/LiveComponentRuntime.php

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
1515
use Symfony\UX\LiveComponent\LiveComponentHydrator;
1616
use Symfony\UX\LiveComponent\Metadata\LiveComponentMetadataFactory;
17+
use Symfony\UX\StimulusBundle\Helper\StimulusHelper;
1718
use Symfony\UX\TwigComponent\ComponentFactory;
1819

1920
/**
@@ -28,6 +29,7 @@ public function __construct(
2829
private ComponentFactory $factory,
2930
private UrlGeneratorInterface $urlGenerator,
3031
private LiveComponentMetadataFactory $metadataFactory,
32+
private StimulusHelper $stimulusHelper,
3133
) {
3234
}
3335

@@ -45,4 +47,29 @@ public function getComponentUrl(string $name, array $props = []): string
4547

4648
return $this->urlGenerator->generate($metadata->get('route'), $params, $metadata->get('url_reference_type'));
4749
}
50+
51+
public function liveAction(string $actionName, array $parameters = [], array $modifiers = [], ?string $event = null): string
52+
{
53+
$attributes = $this->stimulusHelper->createStimulusAttributes();
54+
55+
$modifiers = array_map(static function (string $key, mixed $value) {
56+
return $value ? \sprintf('%s(%s)', $key, $value) : $key;
57+
}, array_keys($modifiers), array_values($modifiers));
58+
59+
$parts = explode(':', $actionName);
60+
61+
$parameters['action'] = $modifiers ? \sprintf('%s|%s', implode('|', $modifiers), $parts[0]) : $parts[0];
62+
63+
array_shift($parts);
64+
65+
$name = 'action';
66+
67+
if (\count($parts) > 0) {
68+
$name .= ':'.implode(':', $parts);
69+
}
70+
71+
$attributes->addAction('live', $name, $event, $parameters);
72+
73+
return (string) $attributes;
74+
}
4875
}

src/LiveComponent/tests/Fixtures/Kernel.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
use Symfony\UX\LiveComponent\Tests\Fixtures\Component\Component1;
3232
use Symfony\UX\LiveComponent\Tests\Fixtures\Serializer\Entity2Normalizer;
3333
use Symfony\UX\LiveComponent\Tests\Fixtures\Serializer\MoneyNormalizer;
34+
use Symfony\UX\StimulusBundle\StimulusBundle;
3435
use Symfony\UX\TwigComponent\TwigComponentBundle;
3536
use Twig\Environment;
3637
use Twig\Loader\FilesystemLoader;
@@ -72,6 +73,7 @@ public function registerBundles(): iterable
7273
yield new SecurityBundle();
7374
yield new TwigComponentBundle();
7475
yield new LiveComponentBundle();
76+
yield new StimulusBundle();
7577
yield new ZenstruckFoundryBundle();
7678
}
7779

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\UX\LiveComponent\Tests\Unit;
13+
14+
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
15+
use Symfony\UX\LiveComponent\Twig\LiveComponentRuntime;
16+
17+
final class LiveComponentRuntimeTest extends KernelTestCase
18+
{
19+
public function testGetLiveAction(): void
20+
{
21+
$runtime = self::getContainer()->get('ux.live_component.twig.component_runtime');
22+
\assert($runtime instanceof LiveComponentRuntime);
23+
24+
$props = $runtime->liveAction('action-name');
25+
$this->assertSame('data-action="live#action" data-live-action-param="action-name"', $props);
26+
27+
$props = $runtime->liveAction('action-name', ['prop1' => 'val1', 'someProp' => 'val2']);
28+
$this->assertSame('data-action="live#action" data-live-prop1-param="val1" data-live-some-prop-param="val2" data-live-action-param="action-name"', $props);
29+
30+
$props = $runtime->liveAction('action-name', ['prop1' => 'val1', 'prop2' => 'val2'], ['debounce' => 300]);
31+
$this->assertSame('data-action="live#action" data-live-prop1-param="val1" data-live-prop2-param="val2" data-live-action-param="debounce(300)|action-name"', \html_entity_decode($props));
32+
$this->assertSame('data-action="live#action" data-live-prop1-param="val1" data-live-prop2-param="val2" data-live-action-param="debounce&#x28;300&#x29;&#x7C;action-name"', $props);
33+
34+
$props = $runtime->liveAction('action-name:prevent', ['pro1' => 'val1', 'prop2' => 'val2'], ['debounce' => 300]);
35+
$this->assertSame('data-action="live#action:prevent" data-live-pro1-param="val1" data-live-prop2-param="val2" data-live-action-param="debounce(300)|action-name"', \html_entity_decode($props));
36+
$this->assertSame('data-action="live#action&#x3A;prevent" data-live-pro1-param="val1" data-live-prop2-param="val2" data-live-action-param="debounce&#x28;300&#x29;&#x7C;action-name"', $props);
37+
38+
$props = $runtime->liveAction('action-name:prevent', [], ['debounce' => 300]);
39+
$this->assertSame('data-action="live#action:prevent" data-live-action-param="debounce(300)|action-name"', \html_entity_decode($props));
40+
41+
$props = $runtime->liveAction('action-name', [], [], 'keydown.esc');
42+
$this->assertSame('data-action="keydown.esc->live#action" data-live-action-param="action-name"', \html_entity_decode($props));
43+
}
44+
}

0 commit comments

Comments
 (0)