Skip to content

Commit 64388ae

Browse files
committed
[LiveComponent] New placeholder macro to generate defer/lazy skeleton
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 leads to code duplicates. 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. And you'll get the props of your component as parameters
1 parent 70f8ca4 commit 64388ae

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)