@@ -2350,6 +2350,8 @@ a model in a child updates, it won't also update that model in its parent
23502350The parent-child system is *smart *. And with a few tricks, you can make
23512351it behave exactly like you need.
23522352
2353+ .. _child-component-independent-rerender :
2354+
23532355Each component re-renders independent of one another
23542356~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
23552357
@@ -2631,6 +2633,27 @@ Notice that ``MarkdownTextarea`` allows a dynamic ``name``
26312633attribute to be passed in. This makes that component re-usable in any
26322634form.
26332635
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+
26342657Rendering Quirks with List of Embedded Components
26352658~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
26362659
@@ -2648,10 +2671,10 @@ to that component:
26482671
26492672.. code-block :: twig
26502673
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,
26552678 key: lineItem.id,
26562679 }) }}
26572680 {% endfor %}
@@ -2661,6 +2684,128 @@ which will be used to identify each child component. You can
26612684also pass in a ``data-live-id `` attribute directly, but ``key `` is
26622685a bit more convenient.
26632686
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+
26642809Advanced Functionality
26652810----------------------
26662811
@@ -2694,6 +2839,40 @@ The system doesn't handle every edge case, so here are some things to keep in mi
26942839 that change is **lost **: the element will be re-added in its original location
26952840 during the next re-render.
26962841
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+
26972876Skipping Updating Certain Elements
26982877~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
26992878
0 commit comments