@@ -2631,6 +2631,23 @@ Notice that ``MarkdownTextarea`` allows a dynamic ``name``
2631
2631
attribute to be passed in. This makes that component re-usable in any
2632
2632
form.
2633
2633
2634
+ Rendering Quirks with List of Elements
2635
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2636
+
2637
+ If you're rendering a list of elements in your component, to help LiveComponents
2638
+ understand which element is which between re-renders (i.e. if something re-orders
2639
+ or removes some of those elements), you can add a ``data-live-id `` attribute to
2640
+ each element
2641
+
2642
+ .. code-block :: twig
2643
+
2644
+ {# templates/components/Invoice.html.twig #}
2645
+ {% for lineItem in lineItems %}
2646
+ <div data-live-id="{{ lineItem.id }}">
2647
+ {{ lineItem.name }}
2648
+ </div>
2649
+ {% endfor %}
2650
+
2634
2651
Rendering Quirks with List of Embedded Components
2635
2652
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2636
2653
@@ -2650,8 +2667,8 @@ to that component:
2650
2667
2651
2668
{# templates/components/Invoice.html.twig #}
2652
2669
{% for lineItem in lineItems %}
2653
- {{ component('invoice_line_item ', {
2654
- productId : lineItem.productId ,
2670
+ {{ component('InvoiceLineItem ', {
2671
+ lineItem : lineItem,
2655
2672
key: lineItem.id,
2656
2673
}) }}
2657
2674
{% endfor %}
@@ -2661,6 +2678,125 @@ which will be used to identify each child component. You can
2661
2678
also pass in a ``data-live-id `` attribute directly, but ``key `` is
2662
2679
a bit more convenient.
2663
2680
2681
+ Tricks with a Loop + a "New" Item
2682
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2683
+
2684
+ Let's get fancier. Suppose you're rendering all of the line items
2685
+ inside of an `InvoiceCreator ` component. After the loop, you might
2686
+ decide to render one more component to create a *new * line item.
2687
+ In that case, you can pass in a ``key `` set to something like ``new_line_item ``:
2688
+
2689
+ .. code-block :: twig
2690
+
2691
+ {{ component('InvoiceLineItem', {
2692
+ key: 'new_line_item',
2693
+ }) }}
2694
+
2695
+ Imagine you also have a ``LiveAction `` inside of ``InvoiceLineItem ``
2696
+ that saves the new line item to the database. To be extra fancy,
2697
+ it emits a ``lineItem:created `` event to the parent::
2698
+
2699
+ // src/Twig/InvoiceLineItem.php
2700
+ // ...
2701
+
2702
+ #[AsLiveComponent]
2703
+ final class InvoiceLineItem
2704
+ {
2705
+ // ...
2706
+
2707
+ #[LiveProp]
2708
+ #[Valid]
2709
+ public InvoiceLineItem $lineItem;
2710
+
2711
+ #[PostMount]
2712
+ public function postMount(): void
2713
+ {
2714
+ if(!$this->lineItem) {
2715
+ $this->lineItem = new InvoiceLineItem();
2716
+ }
2717
+ }
2718
+
2719
+ #[LiveAction]
2720
+ public function save(EntityManagerInterface $entityManager)
2721
+ {
2722
+ if (!$this->lineItem->getId()) {
2723
+ $this->emit('lineItem:created', $this->lineItem);
2724
+ }
2725
+
2726
+ $entityManager->persist($this->lineItem);
2727
+ $entityManager->flush();
2728
+ }
2729
+ }
2730
+
2731
+ Finally, the parent ``InvoiceCreator `` component listens to this
2732
+ so that it can re-render the line items (which will now contain the
2733
+ newly-saved item)::
2734
+
2735
+ // src/Twig/InvoiceCreator.php
2736
+ // ...
2737
+
2738
+ #[AsLiveComponent]
2739
+ final class InvoiceCreator
2740
+ {
2741
+ // ...
2742
+
2743
+ #[LiveListener('lineItem:created')]
2744
+ public function addLineItem()
2745
+ {
2746
+ // no need to do anything here: the component will re-render
2747
+ }
2748
+ }
2749
+
2750
+ This will work beautifully: when a new line item is saved, the ``InvoiceCreator ``
2751
+ component will re-render and the newly saved line item will be displayed along
2752
+ with the extra ``new_line_item `` component at the bottom.
2753
+
2754
+ But something surprising might happen: the ``new_line_item `` component won't
2755
+ update! It will *keep * the data and props that were there a moment ago (i.e. the
2756
+ form fields will still have data in them) instead of rendering a fresh, empty component.
2757
+
2758
+ Why? When live components re-renders, it thinks the existing ``key: new_line_item ``
2759
+ component on the page is the *same * new component that it's about to render. And
2760
+ because the props passed into that component haven't changed, it doesn't see any
2761
+ reason to re-render it.
2762
+
2763
+ To fix this, you haev two options:
2764
+
2765
+ 1. Make the ``key `` dynamic so it will be different after adding a new item::
2766
+
2767
+ .. code-block :: twig
2768
+
2769
+ {{ component('InvoiceLineItem', {
2770
+ key: 'new_line_item_' lineItems|length,
2771
+ }) }}
2772
+
2773
+ 2. Reset the state of the ``InvoiceLineItem `` component after it's saved::
2774
+
2775
+ // src/Twig/InvoiceLineItem.php
2776
+
2777
+ // ...
2778
+ class InvoiceLineItem
2779
+ {
2780
+ // ...
2781
+
2782
+ #[LiveAction]
2783
+ public function save(EntityManagerInterface $entityManager)
2784
+ {
2785
+ $isNew = null === $this->lineItem->getId();
2786
+
2787
+ $entityManager->persist($this->lineItem);
2788
+ $entityManager->flush();
2789
+
2790
+ if ($isNew) {
2791
+ // reset the state of this component
2792
+ $this->emit('lineItem:created', $this->lineItem);
2793
+ $this->lineItem = new InvoiceLineItem();
2794
+ // if you're using ValidatableComponentTrait
2795
+ $this->clearValidation();
2796
+ }
2797
+ }
2798
+ }
2799
+
2664
2800
Advanced Functionality
2665
2801
----------------------
2666
2802
0 commit comments