@@ -2350,6 +2350,8 @@ a model in a child updates, it won't also update that model in its parent
2350
2350
The parent-child system is *smart *. And with a few tricks, you can make
2351
2351
it behave exactly like you need.
2352
2352
2353
+ .. _child-component-independent-rerender :
2354
+
2353
2355
Each component re-renders independent of one another
2354
2356
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2355
2357
@@ -2631,6 +2633,27 @@ Notice that ``MarkdownTextarea`` allows a dynamic ``name``
2631
2633
attribute to be passed in. This makes that component re-usable in any
2632
2634
form.
2633
2635
2636
+ .. _rendering-loop-of-elements :
2637
+
2638
+ Rendering Quirks with List of Elements
2639
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2640
+
2641
+ If you're rendering a list of elements in your component, to help LiveComponents
2642
+ understand which element is which between re-renders (i.e. if something re-orders
2643
+ or removes some of those elements), you can add a ``data-live-id `` attribute to
2644
+ each element
2645
+
2646
+ .. code-block :: html+twig
2647
+
2648
+ {# templates/components/Invoice.html.twig #}
2649
+ {% for lineItem in lineItems %}
2650
+ <div data-live-id="{{ lineItem.id }}">
2651
+ {{ lineItem.name }}
2652
+ </div>
2653
+ {% endfor %}
2654
+
2655
+ .. _key-prop :
2656
+
2634
2657
Rendering Quirks with List of Embedded Components
2635
2658
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2636
2659
@@ -2648,10 +2671,10 @@ to that component:
2648
2671
2649
2672
.. code-block :: twig
2650
2673
2651
- {# templates/components/Invoice .html.twig #}
2652
- {% for lineItem in lineItems %}
2653
- {{ component('invoice_line_item ', {
2654
- productId : lineItem.productId ,
2674
+ {# templates/components/InvoiceCreator .html.twig #}
2675
+ {% for lineItem in invoice. lineItems %}
2676
+ {{ component('InvoiceLineItemForm ', {
2677
+ lineItem : lineItem,
2655
2678
key: lineItem.id,
2656
2679
}) }}
2657
2680
{% endfor %}
@@ -2661,6 +2684,128 @@ which will be used to identify each child component. You can
2661
2684
also pass in a ``data-live-id `` attribute directly, but ``key `` is
2662
2685
a bit more convenient.
2663
2686
2687
+ .. _rendering-loop-new-element :
2688
+
2689
+ Tricks with a Loop + a "New" Item
2690
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2691
+
2692
+ Let's get fancier. After looping over the current line items, you
2693
+ decide to render one more component to create a *new * line item.
2694
+ In that case, you can pass in a ``key `` set to something like ``new_line_item ``:
2695
+
2696
+ .. code-block :: twig
2697
+
2698
+ {# templates/components/InvoiceCreator.html.twig #}
2699
+ // ... loop and render the existing line item components
2700
+
2701
+ {{ component('InvoiceLineItemForm', {
2702
+ key: 'new_line_item',
2703
+ }) }}
2704
+
2705
+ Imagine you also have a ``LiveAction `` inside of ``InvoiceLineItemForm ``
2706
+ that saves the new line item to the database. To be extra fancy,
2707
+ it emits a ``lineItem:created `` event to the parent::
2708
+
2709
+ // src/Twig/InvoiceLineItemForm.php
2710
+ // ...
2711
+
2712
+ #[AsLiveComponent]
2713
+ final class InvoiceLineItemForm
2714
+ {
2715
+ // ...
2716
+
2717
+ #[LiveProp]
2718
+ #[Valid]
2719
+ public ?InvoiceLineItem $lineItem = null;
2720
+
2721
+ #[PostMount]
2722
+ public function postMount(): void
2723
+ {
2724
+ if(!$this->lineItem) {
2725
+ $this->lineItem = new InvoiceLineItem();
2726
+ }
2727
+ }
2728
+
2729
+ #[LiveAction]
2730
+ public function save(EntityManagerInterface $entityManager)
2731
+ {
2732
+ if (!$this->lineItem->getId()) {
2733
+ $this->emit('lineItem:created', $this->lineItem);
2734
+ }
2735
+
2736
+ $entityManager->persist($this->lineItem);
2737
+ $entityManager->flush();
2738
+ }
2739
+ }
2740
+
2741
+ Finally, the parent ``InvoiceCreator `` component listens to this
2742
+ so that it can re-render the line items (which will now contain the
2743
+ newly-saved item)::
2744
+
2745
+ // src/Twig/InvoiceCreator.php
2746
+ // ...
2747
+
2748
+ #[AsLiveComponent]
2749
+ final class InvoiceCreator
2750
+ {
2751
+ // ...
2752
+
2753
+ #[LiveListener('lineItem:created')]
2754
+ public function addLineItem()
2755
+ {
2756
+ // no need to do anything here: the component will re-render
2757
+ }
2758
+ }
2759
+
2760
+ This will work beautifully: when a new line item is saved, the ``InvoiceCreator ``
2761
+ component will re-render and the newly saved line item will be displayed along
2762
+ with the extra ``new_line_item `` component at the bottom.
2763
+
2764
+ But something surprising might happen: the ``new_line_item `` component won't
2765
+ update! It will *keep * the data and props that were there a moment ago (i.e. the
2766
+ form fields will still have data in them) instead of rendering a fresh, empty component.
2767
+
2768
+ Why? When live components re-renders, it thinks the existing ``key: new_line_item ``
2769
+ component on the page is the *same * new component that it's about to render. And
2770
+ because the props passed into that component haven't changed, it doesn't see any
2771
+ reason to re-render it.
2772
+
2773
+ To fix this, you have two options:
2774
+
2775
+ 1. Make the ``key `` dynamic so it will be different after adding a new item::
2776
+
2777
+ .. code-block :: twig
2778
+
2779
+ {{ component('InvoiceLineItemForm', {
2780
+ key: 'new_line_item_'~lineItems|length,
2781
+ }) }}
2782
+
2783
+ 2. Reset the state of the ``InvoiceLineItemForm `` component after it's saved::
2784
+
2785
+ // src/Twig/InvoiceLineItemForm.php
2786
+ // ...
2787
+ class InvoiceLineItemForm
2788
+ {
2789
+ // ...
2790
+
2791
+ #[LiveAction]
2792
+ public function save(EntityManagerInterface $entityManager)
2793
+ {
2794
+ $isNew = null === $this->lineItem->getId();
2795
+
2796
+ $entityManager->persist($this->lineItem);
2797
+ $entityManager->flush();
2798
+
2799
+ if ($isNew) {
2800
+ // reset the state of this component
2801
+ $this->emit('lineItem:created', $this->lineItem);
2802
+ $this->lineItem = new InvoiceLineItem();
2803
+ // if you're using ValidatableComponentTrait
2804
+ $this->clearValidation();
2805
+ }
2806
+ }
2807
+ }
2808
+
2664
2809
Advanced Functionality
2665
2810
----------------------
2666
2811
@@ -2694,6 +2839,40 @@ The system doesn't handle every edge case, so here are some things to keep in mi
2694
2839
that change is **lost **: the element will be re-added in its original location
2695
2840
during the next re-render.
2696
2841
2842
+ The Mystical data-live-id Attribute
2843
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2844
+
2845
+ The ``data-live-id `` attribute is mentioned several times throughout the documentation
2846
+ to solve various problems. It's usually not needed, but can be the key to solving
2847
+ certain complex problems. But what is it?
2848
+
2849
+ .. note ::
2850
+
2851
+ The :ref: `key prop <key-prop >` is used to create a ``data-live-id `` attribute
2852
+ on child components. So everything in this section applies equally to the
2853
+ ``key `` prop.
2854
+
2855
+ The ``data-live-id `` attribute is a unique identifier for an element or a component.
2856
+ It's used when a component re-renders and helps Live Components "connect" elements
2857
+ or components in the existing HTML with the new HTML. The logic works like this:
2858
+
2859
+ Suppose an element or component in the new HTML has a ``data-live-id="some-id `` attribute.
2860
+ Then:
2861
+
2862
+ A) If there **is ** an element or component with ``data-live-id="some-id" `` in the
2863
+ existing HTML, then the old and new elements/components are considered to be the
2864
+ "same". For elements, the new element will be used to update the old element even
2865
+ if the two elements appear in different places - e.g. like if :ref: `elements are moved <rendering-loop-of-elements >`
2866
+ or re-ordered. For components, because child components render independently
2867
+ from their parent, the existing component will be "left alone" and not re-rendered
2868
+ (unless some ``updateFromParent `` props have changed - see :ref: `child-component-independent-rerender `).
2869
+
2870
+ B) If there is **not ** an element or component with ``data-live-id="some-id" `` in
2871
+ the existing HTML, then the new element or component is considered to be "new".
2872
+ In both cases, the new element or component will be added to the page. If there
2873
+ is a component/element with a ``data-live-id `` attribute that is *not * in the
2874
+ new HTML, that component/element will be removed from the page.
2875
+
2697
2876
Skipping Updating Certain Elements
2698
2877
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2699
2878
0 commit comments