@@ -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
@@ -113,19 +109,15 @@ Suppose you've already built a basic Twig component:
113109// src/Components/RandomNumberComponent.php
114110namespace App\Components;
115111
116- use Symfony\UX\TwigComponent\ComponentInterface ;
112+ use Symfony\UX\TwigComponent\Attribute\AsTwigComponent ;
117113
118- class RandomNumberComponent implements ComponentInterface
114+ # [AsTwigComponent('random_number')]
115+ class RandomNumberComponent
119116{
120117 public function getRandomNumber() : string
121118 {
122119 return rand(0, 1000);
123120 }
124-
125- public static function getComponentName() : string
126- {
127- return 'random_number';
128- }
129121}
130122```
131123
@@ -137,16 +129,18 @@ class RandomNumberComponent implements ComponentInterface
137129```
138130
139131To transform this into a "live" component (i.e. one that
140- can be re-rendered live on the frontend), change your
141- component's interface to ` LiveComponentInterface ` :
132+ can be re-rendered live on the frontend), replace the
133+ component's ` AsTwigComponent ` attribute with ` AsLiveComponent ` :
142134
143135``` diff
144136// src/Components/RandomNumberComponent.php
145137
146- + use Symfony\UX\LiveComponent\LiveComponentInterface;
138+ - use Symfony\UX\TwigComponent\Attribute\AsTwigComponent;
139+ + use Symfony\UX\LiveComponent\Attribute\AsLiveComponent;
147140
148- - class RandomNumberComponent implements ComponentInterface
149- + class RandomNumberComponent implements LiveComponentInterface
141+ - #[AsTwigComponent('random_number')]
142+ - #[AsLiveComponent('random_number')]
143+ class RandomNumberComponent
150144{
151145}
152146```
@@ -193,11 +187,13 @@ namespace App\Components;
193187// ...
194188use Symfony\UX\LiveComponent\Attribute\LiveProp;
195189
196- class RandomNumberComponent implements LiveComponentInterface
190+ #[AsLiveComponent('random_number')]
191+ class RandomNumberComponent
197192{
198- /** @ LiveProp */
193+ #[ LiveProp]
199194 public int $min = 0;
200- /** @LiveProp */
195+
196+ #[LiveProp]
201197 public int $max = 1000;
202198
203199 public function getRandomNumber(): string
@@ -216,14 +212,14 @@ when rendering the component:
216212{{ component('random_number', { min: 5, max: 500 }) }}
217213```
218214
219- But what's up with those ` @ LiveProp` annotations ? A property with
220- the ` @ LiveProp` annotation (or ` LiveProp ` PHP 8 attribute) becomes
221- a "stateful" property for this component. In other words, each time
222- we click the "Generate a new number!" button, when the component
223- re-renders, it will _ remember_ the original values for the ` $min ` and
224- ` $max ` properties and generate a random number between 5 and 500.
225- If you forgot to add ` @ LiveProp` , when the component re-rendered,
226- those two values would _ not_ be set on the object.
215+ But what's up with those ` LiveProp ` attributes ? A property with
216+ the ` LiveProp ` attribute becomes a "stateful" property for this
217+ component. In other words, each time we click the "Generate a
218+ new number!" button, when the component re-renders, it will
219+ _ remember_ the original values for the ` $min ` and ` $max ` properties
220+ and generate a random number between 5 and 500. If you forgot to
221+ add ` LiveProp ` , when the component re-rendered, those two values
222+ would _ not_ be set on the object.
227223
228224In short: LiveProps are "stateful properties": they will always
229225be set when rendering. Most properties will be LiveProps, with
@@ -277,13 +273,14 @@ the `writable=true` option:
277273// src/Components/RandomNumberComponent.php
278274// ...
279275
280- class RandomNumberComponent implements LiveComponentInterface
276+ class RandomNumberComponent
281277{
282- - /** @ LiveProp() */
283- + /** @ LiveProp(writable= true) */
278+ - #[ LiveProp]
279+ + #[ LiveProp(writable: true)]
284280 public int $min = 0;
285- - /** @LiveProp() */
286- + /** @LiveProp(writable=true) */
281+
282+ - #[LiveProp]
283+ + #[LiveProp(writable: true)]
287284 public int $max = 1000;
288285
289286 // ...
@@ -438,8 +435,8 @@ want to add a "Reset Min/Max" button to our "random number"
438435component that, when clicked, sets the min/max numbers back
439436to a default value.
440437
441- First, add a method with a ` LiveAction ` annotation (or PHP 8 attribute)
442- above it that does the work:
438+ First, add a method with a ` LiveAction ` attribute above it that
439+ does the work:
443440
444441``` php
445442// src/Components/RandomNumberComponent.php
@@ -448,13 +445,11 @@ namespace App\Components;
448445// ...
449446use Symfony\UX\LiveComponent\Attribute\LiveAction;
450447
451- class RandomNumberComponent implements LiveComponentInterface
448+ class RandomNumberComponent
452449{
453450 // ...
454451
455- /**
456- * @LiveAction
457- */
452+ #[LiveAction]
458453 public function resetMinMax()
459454 {
460455 $this->min = 0;
@@ -513,13 +508,11 @@ namespace App\Components;
513508// ...
514509use Psr\Log\LoggerInterface;
515510
516- class RandomNumberComponent implements LiveComponentInterface
511+ class RandomNumberComponent
517512{
518513 // ...
519514
520- /**
521- * @LiveAction
522- */
515+ #[LiveAction]
523516 public function resetMinMax(LoggerInterface $logger)
524517 {
525518 $this->min = 0;
@@ -558,13 +551,11 @@ namespace App\Components;
558551// ...
559552use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
560553
561- class RandomNumberComponent extends AbstractController implements LiveComponentInterface
554+ class RandomNumberComponent extends AbstractController
562555{
563556 // ...
564557
565- /**
566- * @LiveAction
567- */
558+ #[LiveAction]
568559 public function resetMinMax()
569560 {
570561 // ...
@@ -694,11 +685,12 @@ use App\Entity\Post;
694685use App\Form\PostType;
695686use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
696687use Symfony\Component\Form\FormInterface;
688+ use Symfony\UX\LiveComponent\Attribute\AsLiveComponent;
697689use Symfony\UX\LiveComponent\Attribute\LiveProp;
698- use Symfony\UX\LiveComponent\LiveComponentInterface;
699690use Symfony\UX\LiveComponent\ComponentWithFormTrait;
700691
701- class PostFormComponent extends AbstractController implements LiveComponentInterface
692+ #[AsLiveComponent('post_form')]
693+ class PostFormComponent extends AbstractController
702694{
703695 use ComponentWithFormTrait;
704696
@@ -708,13 +700,12 @@ class PostFormComponent extends AbstractController implements LiveComponentInter
708700 * Needed so the same form can be re-created
709701 * when the component is re-rendered via Ajax.
710702 *
711- * The fieldName="" option is needed in this situation because
703+ * The ` fieldName` option is needed in this situation because
712704 * the form renders fields with names like `name="post[title]"`.
713- * We set fieldName="" so that this live prop doesn't collide
705+ * We set ` fieldName: ''` so that this live prop doesn't collide
714706 * with that data. The value - initialFormData - could be anything.
715- *
716- * @LiveProp(fieldName="initialFormData")
717707 */
708+ #[LiveProp(fieldName: 'initialFormData')]
718709 public ?Post $post = null;
719710
720711 /**
@@ -725,11 +716,6 @@ class PostFormComponent extends AbstractController implements LiveComponentInter
725716 // we can extend AbstractController to get the normal shortcuts
726717 return $this->createForm(PostType::class, $this->post);
727718 }
728-
729- public static function getComponentName(): string
730- {
731- return 'post_form';
732- }
733719}
734720```
735721
@@ -885,13 +871,11 @@ action to the component:
885871use Doctrine\ORM\EntityManagerInterface;
886872use Symfony\UX\LiveComponent\Attribute\LiveAction;
887873
888- class PostFormComponent extends AbstractController implements LiveComponentInterface
874+ class PostFormComponent extends AbstractController
889875{
890876 // ...
891877
892- /**
893- * @LiveAction()
894- */
878+ #[LiveAction]
895879 public function save(EntityManagerInterface $entityManager)
896880 {
897881 // shortcut to submit the form with form values
@@ -942,20 +926,14 @@ that is being edited:
942926namespace App\Twig\Components;
943927
944928use App\Entity\Post;
929+ use Symfony\UX\LiveComponent\Attribute\AsLiveComponent;
945930use Symfony\UX\LiveComponent\Attribute\LiveProp;
946- use Symfony\UX\LiveComponent\LiveComponentInterface;
947931
948- class EditPostComponent implements LiveComponentInterface
932+ #[AsLiveComponent('edit_post')]
933+ class EditPostComponent
949934{
950- /**
951- * @LiveProp()
952- */
935+ #[LiveProp]
953936 public Post $post;
954-
955- public static function getComponentName(): string
956- {
957- return 'edit_post';
958- }
959937}
960938```
961939
@@ -995,12 +973,10 @@ you can enable it via the `exposed` option:
995973``` diff
996974// ...
997975
998- class EditPostComponent implements LiveComponentInterface
976+ class EditPostComponent
999977{
1000- /**
1001- - * @LiveProp(exposed={})
1002- + * @LiveProp(exposed={"title", "content"})
1003- */
978+ - #[LiveProp]
979+ + #[LiveProp(exposed: ['title', 'content'])]
1004980 public Post $post;
1005981
1006982 // ...
@@ -1030,36 +1006,28 @@ First use the `ValidatableComponentTrait` and add any constraints you need:
10301006
10311007``` php
10321008use App\Entity\User;
1009+ use Symfony\UX\LiveComponent\Attribute\AsLiveComponent;
10331010use Symfony\UX\LiveComponent\Attribute\LiveProp;
1034- use Symfony\UX\LiveComponent\LiveComponentInterface;
10351011use Symfony\UX\LiveComponent\ValidatableComponentTrait;
10361012use Symfony\Component\Validator\Constraints as Assert;
10371013
1038- class EditUserComponent implements LiveComponentInterface
1014+ #[AsLiveComponent('edit_user')]
1015+ class EditUserComponent
10391016{
10401017 use ValidatableComponentTrait;
10411018
1042- /**
1043- * @LiveProp(exposed={"email", "plainPassword"})
1044- * @Assert\Valid()
1045- */
1019+ #[LiveProp(exposed: ['email', 'plainPassword'])]
1020+ #[Assert\Valid]
10461021 public User $user;
10471022
1048- /**
1049- * @LiveProp()
1050- * @Assert\IsTrue()
1051- */
1023+ #[LiveProp]
1024+ #[Assert\IsTrue]
10521025 public bool $agreeToTerms = false;
1053-
1054- public static function getComponentName() : string
1055- {
1056- return 'edit_user';
1057- }
10581026}
10591027```
10601028
1061- Be sure to add the ` @Assert\ IsValid` to any property where you want
1062- the object on that property to also be validated.
1029+ Be sure to add the ` IsValid ` attribute/annotation to any property where
1030+ you want the object on that property to also be validated.
10631031
10641032Thanks to this setup, the component will now be automatically validated
10651033on each render, but in a smart way: a property will only be validated
@@ -1073,13 +1041,12 @@ in an action:
10731041``` php
10741042use Symfony\UX\LiveComponent\Attribute\LiveAction;
10751043
1076- class EditUserComponent implements LiveComponentInterface
1044+ #[AsLiveComponent('edit_user')]
1045+ class EditUserComponent
10771046{
10781047 // ...
10791048
1080- /**
1081- * @LiveAction()
1082- */
1049+ #[LiveAction]
10831050 public function save()
10841051 {
10851052 // this will throw an exception if validation fails
0 commit comments