@@ -15,9 +15,10 @@ A real-time product search component might look like this:
1515// src/Components/ProductSearchComponent.php
1616namespace App\Components;
1717
18- use Symfony\UX\LiveComponent\LiveComponentInterface ;
18+ use Symfony\UX\LiveComponent\Attribute\AsLiveComponent ;
1919
20- class ProductSearchComponent implements LiveComponentInterface
20+ #[AsLiveComponent('product_search')]
21+ class ProductSearchComponent
2122{
2223 public string $query = '';
2324
@@ -33,11 +34,6 @@ class ProductSearchComponent implements LiveComponentInterface
3334 // example method that returns an array of Products
3435 return $this->productRepository->search($this->query);
3536 }
36-
37- public static function getComponentName(): string
38- {
39- return 'product_search';
40- }
4137}
4238```
4339
@@ -103,19 +99,15 @@ Suppose you've already built a basic Twig component:
10399// src/Components/RandomNumberComponent.php
104100namespace App\Components;
105101
106- use Symfony\UX\TwigComponent\ComponentInterface ;
102+ use Symfony\UX\TwigComponent\Attribute\AsTwigComponent ;
107103
108- class RandomNumberComponent implements ComponentInterface
104+ #[AsTwigComponent('random_number')]
105+ class RandomNumberComponent
109106{
110107 public function getRandomNumber(): string
111108 {
112109 return rand(0, 1000);
113110 }
114-
115- public static function getComponentName(): string
116- {
117- return 'random_number';
118- }
119111}
120112```
121113
@@ -127,16 +119,18 @@ class RandomNumberComponent implements ComponentInterface
127119```
128120
129121To transform this into a "live" component (i.e. one that
130- can be re-rendered live on the frontend), change your
131- component's interface to ` LiveComponentInterface ` :
122+ can be re-rendered live on the frontend), replace the
123+ component's ` AsTwigComponent ` attribute with ` AsLiveComponent ` :
132124
133125``` diff
134126// src/Components/RandomNumberComponent.php
135127
136- + use Symfony\UX\LiveComponent\LiveComponentInterface;
128+ - use Symfony\UX\TwigComponent\Attribute\AsTwigComponent;
129+ + use Symfony\UX\LiveComponent\Attribute\AsLiveComponent;
137130
138- - class RandomNumberComponent implements ComponentInterface
139- + class RandomNumberComponent implements LiveComponentInterface
131+ - #[AsTwigComponent('random_number')]
132+ - #[AsLiveComponent('random_number')]
133+ class RandomNumberComponent
140134{
141135}
142136```
@@ -183,11 +177,13 @@ namespace App\Components;
183177// ...
184178use Symfony\UX\LiveComponent\Attribute\LiveProp;
185179
186- class RandomNumberComponent implements LiveComponentInterface
180+ #[AsLiveComponent('random_number')]
181+ class RandomNumberComponent
187182{
188- /** @ LiveProp */
183+ #[ LiveProp]
189184 public int $min = 0;
190- /** @LiveProp */
185+
186+ #[LiveProp]
191187 public int $max = 1000;
192188
193189 public function getRandomNumber(): string
@@ -206,14 +202,14 @@ when rendering the component:
206202{{ component('random_number', { min: 5, max: 500 }) }}
207203```
208204
209- But what's up with those ` @ LiveProp` annotations ? A property with
210- the ` @ LiveProp` annotation (or ` LiveProp ` PHP 8 attribute) becomes
211- a "stateful" property for this component. In other words, each time
212- we click the "Generate a new number!" button, when the component
213- re-renders, it will _ remember_ the original values for the ` $min ` and
214- ` $max ` properties and generate a random number between 5 and 500.
215- If you forgot to add ` @ LiveProp` , when the component re-rendered,
216- those two values would _ not_ be set on the object.
205+ But what's up with those ` LiveProp ` attributes ? A property with
206+ the ` LiveProp ` attribute becomes a "stateful" property for this
207+ component. In other words, each time we click the "Generate a
208+ new number!" button, when the component re-renders, it will
209+ _ remember_ the original values for the ` $min ` and ` $max ` properties
210+ and generate a random number between 5 and 500. If you forgot to
211+ add ` LiveProp ` , when the component re-rendered, those two values
212+ would _ not_ be set on the object.
217213
218214In short: LiveProps are "stateful properties": they will always
219215be set when rendering. Most properties will be LiveProps, with
@@ -267,13 +263,14 @@ the `writable=true` option:
267263// src/Components/RandomNumberComponent.php
268264// ...
269265
270- class RandomNumberComponent implements LiveComponentInterface
266+ class RandomNumberComponent
271267{
272- - /** @ LiveProp() */
273- + /** @ LiveProp(writable= true) */
268+ - #[ LiveProp]
269+ + #[ LiveProp(writable: true)]
274270 public int $min = 0;
275- - /** @LiveProp() */
276- + /** @LiveProp(writable=true) */
271+
272+ - #[LiveProp]
273+ + #[LiveProp(writable: true)]
277274 public int $max = 1000;
278275
279276 // ...
@@ -428,8 +425,8 @@ want to add a "Reset Min/Max" button to our "random number"
428425component that, when clicked, sets the min/max numbers back
429426to a default value.
430427
431- First, add a method with a ` LiveAction ` annotation (or PHP 8 attribute)
432- above it that does the work:
428+ First, add a method with a ` LiveAction ` attribute above it that
429+ does the work:
433430
434431``` php
435432// src/Components/RandomNumberComponent.php
@@ -438,13 +435,11 @@ namespace App\Components;
438435// ...
439436use Symfony\UX\LiveComponent\Attribute\LiveAction;
440437
441- class RandomNumberComponent implements LiveComponentInterface
438+ class RandomNumberComponent
442439{
443440 // ...
444441
445- /**
446- * @LiveAction
447- */
442+ #[LiveAction]
448443 public function resetMinMax()
449444 {
450445 $this->min = 0;
@@ -503,13 +498,11 @@ namespace App\Components;
503498// ...
504499use Psr\Log\LoggerInterface;
505500
506- class RandomNumberComponent implements LiveComponentInterface
501+ class RandomNumberComponent
507502{
508503 // ...
509504
510- /**
511- * @LiveAction
512- */
505+ #[LiveAction]
513506 public function resetMinMax(LoggerInterface $logger)
514507 {
515508 $this->min = 0;
@@ -548,13 +541,11 @@ namespace App\Components;
548541// ...
549542use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
550543
551- class RandomNumberComponent extends AbstractController implements LiveComponentInterface
544+ class RandomNumberComponent extends AbstractController
552545{
553546 // ...
554547
555- /**
556- * @LiveAction
557- */
548+ #[LiveAction]
558549 public function resetMinMax()
559550 {
560551 // ...
@@ -684,11 +675,12 @@ use App\Entity\Post;
684675use App\Form\PostType;
685676use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
686677use Symfony\Component\Form\FormInterface;
678+ use Symfony\UX\LiveComponent\Attribute\AsLiveComponent;
687679use Symfony\UX\LiveComponent\Attribute\LiveProp;
688- use Symfony\UX\LiveComponent\LiveComponentInterface;
689680use Symfony\UX\LiveComponent\ComponentWithFormTrait;
690681
691- class PostFormComponent extends AbstractController implements LiveComponentInterface
682+ #[AsLiveComponent('post_form')]
683+ class PostFormComponent extends AbstractController
692684{
693685 use ComponentWithFormTrait;
694686
@@ -698,13 +690,12 @@ class PostFormComponent extends AbstractController implements LiveComponentInter
698690 * Needed so the same form can be re-created
699691 * when the component is re-rendered via Ajax.
700692 *
701- * The fieldName="" option is needed in this situation because
693+ * The ` fieldName` option is needed in this situation because
702694 * the form renders fields with names like `name="post[title]"`.
703- * We set fieldName="" so that this live prop doesn't collide
695+ * We set ` fieldName: ''` so that this live prop doesn't collide
704696 * with that data. The value - initialFormData - could be anything.
705- *
706- * @LiveProp(fieldName="initialFormData")
707697 */
698+ #[LiveProp(fieldName: 'initialFormData')]
708699 public ?Post $post = null;
709700
710701 /**
@@ -715,11 +706,6 @@ class PostFormComponent extends AbstractController implements LiveComponentInter
715706 // we can extend AbstractController to get the normal shortcuts
716707 return $this->createForm(PostType::class, $this->post);
717708 }
718-
719- public static function getComponentName(): string
720- {
721- return 'post_form';
722- }
723709}
724710```
725711
@@ -875,13 +861,11 @@ action to the component:
875861use Doctrine\ORM\EntityManagerInterface;
876862use Symfony\UX\LiveComponent\Attribute\LiveAction;
877863
878- class PostFormComponent extends AbstractController implements LiveComponentInterface
864+ class PostFormComponent extends AbstractController
879865{
880866 // ...
881867
882- /**
883- * @LiveAction()
884- */
868+ #[LiveAction]
885869 public function save(EntityManagerInterface $entityManager)
886870 {
887871 // shortcut to submit the form with form values
@@ -932,20 +916,14 @@ that is being edited:
932916namespace App\Twig\Components;
933917
934918use App\Entity\Post;
919+ use Symfony\UX\LiveComponent\Attribute\AsLiveComponent;
935920use Symfony\UX\LiveComponent\Attribute\LiveProp;
936- use Symfony\UX\LiveComponent\LiveComponentInterface;
937921
938- class EditPostComponent implements LiveComponentInterface
922+ #[AsLiveComponent('edit_post')]
923+ class EditPostComponent
939924{
940- /**
941- * @LiveProp()
942- */
925+ #[LiveProp]
943926 public Post $post;
944-
945- public static function getComponentName(): string
946- {
947- return 'edit_post';
948- }
949927}
950928```
951929
@@ -985,12 +963,10 @@ you can enable it via the `exposed` option:
985963``` diff
986964// ...
987965
988- class EditPostComponent implements LiveComponentInterface
966+ class EditPostComponent
989967{
990- /**
991- - * @LiveProp(exposed={})
992- + * @LiveProp(exposed={"title", "content"})
993- */
968+ - #[LiveProp]
969+ + #[LiveProp(exposed: ['title', 'content'])]
994970 public Post $post;
995971
996972 // ...
@@ -1020,36 +996,28 @@ First use the `ValidatableComponentTrait` and add any constraints you need:
1020996
1021997``` php
1022998use App\Entity\User;
999+ use Symfony\UX\LiveComponent\Attribute\AsLiveComponent;
10231000use Symfony\UX\LiveComponent\Attribute\LiveProp;
1024- use Symfony\UX\LiveComponent\LiveComponentInterface;
10251001use Symfony\UX\LiveComponent\ValidatableComponentTrait;
10261002use Symfony\Component\Validator\Constraints as Assert;
10271003
1028- class EditUserComponent implements LiveComponentInterface
1004+ #[AsLiveComponent('edit_user')]
1005+ class EditUserComponent
10291006{
10301007 use ValidatableComponentTrait;
10311008
1032- /**
1033- * @LiveProp(exposed={"email", "plainPassword"})
1034- * @Assert\Valid()
1035- */
1009+ #[LiveProp(exposed: ['email', 'plainPassword'])]
1010+ #[Assert\Valid]
10361011 public User $user;
10371012
1038- /**
1039- * @LiveProp()
1040- * @Assert\IsTrue()
1041- */
1013+ #[LiveProp]
1014+ #[Assert\IsTrue]
10421015 public bool $agreeToTerms = false;
1043-
1044- public static function getComponentName() : string
1045- {
1046- return 'edit_user';
1047- }
10481016}
10491017```
10501018
1051- Be sure to add the ` @Assert\ IsValid` to any property where you want
1052- the object on that property to also be validated.
1019+ Be sure to add the ` IsValid ` attribute/annotation to any property where
1020+ you want the object on that property to also be validated.
10531021
10541022Thanks to this setup, the component will now be automatically validated
10551023on each render, but in a smart way: a property will only be validated
@@ -1063,13 +1031,12 @@ in an action:
10631031``` php
10641032use Symfony\UX\LiveComponent\Attribute\LiveAction;
10651033
1066- class EditUserComponent implements LiveComponentInterface
1034+ #[AsLiveComponent('edit_user')]
1035+ class EditUserComponent
10671036{
10681037 // ...
10691038
1070- /**
1071- * @LiveAction()
1072- */
1039+ #[LiveAction]
10731040 public function save()
10741041 {
10751042 // this will throw an exception if validation fails
0 commit comments