Skip to content

Commit 9c7a811

Browse files
committed
feature #1532 [LiveComponent] New placeholder macro to generate defer/lazy skeleton (smnandre)
This PR was squashed before being merged into the 2.x branch. Discussion ---------- [LiveComponent] New placeholder macro to generate defer/lazy skeleton | Q | A | ------------- | --- | Bug fix? |no | New feature? | yes | License | MIT When you use defer/lazy component, you can customize the "loading" content in the calling template. But for components that you reuse on multiple pages, it can lead to code duplication. Moreover, you cannot adapt the size / content depending on the props. So... introducing the "`placeholder`" macro. If you define such macro in your lazy component template, it will be called to generate this temporary content. ```twig {# templates/components/FooBar.html.twig #} <div {{ attributes }}> {# ... #} {# your live component code #} {# ... #} </div> {% macro placeholder(props) %} {# This code will only be visible in the "loading" content #} <span class="loading-row"></span> {% endmacro %} ``` Bonus, you'll get the props of your component as parameters. So let's say you have a live component which load N comments behind an article. When you set this prop to your live component, it will be passed to the placeholder macro, allowing you to fill the good amount of space / loading blocs in the HTML ```twig <twig:Comments n="5" /> ``` ```twig {# templates/components/Comments.html.twig #} <div {{ attributes }} ... </div> {% macro placeholder(props) %} {% for i in 1..props.n %} <span class="loading-block"></span> {% endfor %} {% endmacro %} ``` What do you think ? Commits ------- 2130326 [LiveComponent] New placeholder macro to generate defer/lazy skeleton
2 parents a6e426a + 2130326 commit 9c7a811

9 files changed

+60
-2
lines changed

src/LiveComponent/src/EventListener/DeferLiveComponentSubscriber.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ public function onPreRender(PreRenderEvent $event): void
5252
return;
5353
}
5454

55+
$componentTemplate = $event->getMetadata()->getTemplate();
5556
$event->setTemplate('@LiveComponent/deferred.html.twig');
5657

5758
$variables = $event->getVariables();
@@ -66,6 +67,8 @@ public function onPreRender(PreRenderEvent $event): void
6667
$variables['loadingTag'] = $mountedComponent->getExtraMetadata('loading-tag');
6768
}
6869

70+
$variables['componentTemplate'] = $componentTemplate;
71+
6972
$event->setVariables($variables);
7073
}
7174

src/LiveComponent/src/Test/TestLiveComponent.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ public function __construct(
4343
private UrlGeneratorInterface $router,
4444
) {
4545
$this->client->catchExceptions(false);
46-
$this->data['attributes']['data-live-id'] ??= 'in-a-real-scenario-it-would-already-have-one---provide-one-yourself-if-needed';
46+
$this->data['attributes']['id'] ??= 'in-a-real-scenario-it-would-already-have-one---provide-one-yourself-if-needed';
4747
}
4848

4949
public function render(): RenderedComponent

src/LiveComponent/templates/deferred.html.twig

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22
{% block loadingContent %}
33
{% if loadingTemplate != null %}
44
{{ include(loadingTemplate) }}
5+
{% else %}
6+
{% from componentTemplate import placeholder %}
7+
{% if placeholder is defined %}
8+
{{ placeholder(__props|default) }}
9+
{% endif %}
510
{% endif %}
611
{% endblock %}
712
</{{ loadingTag }}>
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Symfony\UX\LiveComponent\Tests\Fixtures\Component;
6+
7+
use Symfony\UX\LiveComponent\Attribute\AsLiveComponent;
8+
use Symfony\UX\LiveComponent\Attribute\LiveProp;
9+
use Symfony\UX\LiveComponent\DefaultActionTrait;
10+
11+
#[AsLiveComponent('deferred_component_with_placeholder', method: 'get')]
12+
final class DeferredComponentWithPlaceholder
13+
{
14+
use DefaultActionTrait;
15+
16+
#[LiveProp]
17+
public int $rows = 6;
18+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<div {{ attributes }}>
2+
{# for row in this.rows ... #}
3+
</div>
4+
5+
{% macro placeholder(props) %}
6+
{%- for i in 1..props.rows -%}
7+
<span class="loading-row"></span>
8+
{%- endfor -%}
9+
{% endmacro %}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<twig:deferred_component_with_placeholder rows="2" />
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<twig:deferred_component_with_placeholder rows="2" defer />

src/LiveComponent/tests/Functional/EventListener/DeferLiveComponentSubscriberTest.php

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,27 @@ public function testItIncludesGivenTemplateWhileLoadingDeferredComponent(): void
7777
$this->assertStringContainsString('Long awaited data', $div->html());
7878
}
7979

80+
public function testItIncludesComponentTemplateBlockAsPlaceholder(): void
81+
{
82+
$div = $this->browser()
83+
->visit('/render-template/render_deferred_component_with_placeholder')
84+
->assertSuccessful()
85+
->crawler()
86+
->filter('div');
87+
88+
$this->assertSame('<span class="loading-row"></span><span class="loading-row"></span>', trim($div->html()));
89+
}
90+
91+
public function testItDoesNotIncludesPlaceholderWhenRendered(): void
92+
{
93+
$div = $this->browser()
94+
->visit('/render-template/render_component_with_placeholder')
95+
->assertSuccessful()
96+
->crawler();
97+
98+
$this->assertStringNotContainsString('<span class="loading-row">', $div->html());
99+
}
100+
80101
public function testItAllowsToSetCustomLoadingHtmlTag(): void
81102
{
82103
$crawler = $this->browser()

src/LiveComponent/tests/Functional/EventListener/InterceptChildComponentRenderSubscriberTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ public function testItUsesKeysToRenderChildrenLiveIds(): void
128128
->assertSuccessful()
129129
->assertHtml()
130130
->assertElementCount('ul li', 3)
131-
// check for the live-id we expect based on the key
131+
// check for the id we expect based on the key
132132
->assertContains('id="live-521026374-the-key0"')
133133
->assertNotContains('key="the-key0"')
134134
->visit($urlWithChangedFingerprints)

0 commit comments

Comments
 (0)