symfony / ux

Symfony UX initiative: a JavaScript ecosystem for Symfony
https://ux.symfony.com/
MIT License
820 stars 297 forks source link

[LiveComponent] Forever Scroll #1585

Closed ag-erwan closed 5 months ago

ag-erwan commented 6 months ago

Good morning,

I would like to be able to create a system with Live component to be able to load 50 data on the page and when scrolling 50 other data appear. Or an infinite data load How to do it knowing that the data I display with ux live component precisely

WebMamba commented 6 months ago

We actually talk about this topic in here #1515 😁

ag-erwan commented 6 months ago

It's not quite the same, because I would like when I scroll it to display the rest of the data. here is an example,

// src/Components/Products.php

public function __construct(private ProductRepository $productRepository){}

public function getProducts(): array
{
      return $this->productRepository->findAll();
}
<ul>
        {% for product in this.products %}
            <li>{{ product.name }}</li>
        {% endfor %}
</ul>

I retrieve the list of all my products, I have 2000 I do not want it to load my 2000 products.

smnandre commented 6 months ago

I have a demo incoming next week to show you how to do that. If you cannot wait i can send you some quick explanation.

ag-erwan commented 6 months ago

Hello @smnandre,

Yes, I would like a quick explanation if you have 5 minutes. thanks in advance

smnandre commented 6 months ago

I will post there a short example ~tonight~ tomorrow then :)

ag-erwan commented 6 months ago

I will post there a short example ~tonight~ tomorrow then :)

Good morning @smnandre Would you have 5 minutes to send me a demo regarding my previous request?

smnandre commented 6 months ago

I'm working on it during my launch break.. ;)

You can look this upcoming demo: https://github.com/smnandre/ux/tree/site/demo-infinite-scroll

https://127.0.0.1:8000/demos/live-component/infinite-scroll

Look in "InfiniteCollection" template / component

The idea is to have a custom id for the current page and add data-live-ignore

For the following pages always add a fake previous page.

And wrap all this with an id you'll change when the "context" change (there, the search query).

Sorry i don't have more time right now, but i'll answer later if you have some questions.

WebMamba commented 6 months ago

But just to complete the @smnandre answer the most important line is this one: https://github.com/smnandre/ux/blob/dedc2aa0f4aaeabbe834149b774998e5ad27a8bf/ux.symfony.com/templates/components/InfiniteCollection.html.twig#L24 Adding this live-ignore attribute is like saying to your live component don't replace this div, so the live component just appends the new result instead of morphing it.

ag-erwan commented 6 months ago

Hello friends,

Just to inform you that I will be waiting for version 2.17 of live component. I think it will be simpler. Thanks for the demo @smnandre When do you think 2.17 will be available?

smnandre commented 6 months ago

@ag-erwan Happy if that helped you :) I'll try to publish it soon-ish, but bug fixes/features always take priority over demos..

Regarding 2.17, I anticipate it could be around the end of the month (without any guarantee)

smnandre commented 6 months ago

If you need, you always can use 2.x-dev branch until then !

ag-erwan commented 6 months ago

Hello team Just to let you know that I have found an alternative solution. Instead of going with an infinite scroll, I went with a pagination that I linked to ux live component here is the code if it can help some of you.

// template/components/paginator.html.twig

{% if pageCount > 1 %}
    <div class="inline-block">
        <div class="inline-flex -space-x-px text-base h-10">
            {% if first is defined and current != first %}
                <button data-action="live#action" data-live-action-param="nextPreviousPage" data-live-page-param="{{first}}" class="flex items-center justify-center px-4 h-10 ms-0 leading-tight font-bold text-gray-500 bg-white border border-e-0 border-gray-300 rounded-s-lg hover:bg-gray-100 hover:text-gray-700">&lt;&lt;</button>
            {% endif %}

            {% if previous is defined %}
                <button data-action="live#action" data-live-action-param="nextPreviousPage" data-live-page-param="{{previous}}" class="flex items-center justify-center px-4 h-10 leading-tight font-bold text-gray-500 bg-white border border-gray-300 hover:bg-gray-100 hover:text-gray-700">&lt;</button>
            {% endif %}

            {% for page in pagesInRange %}
                {% if page != current %}
                    <button data-action="live#action" data-live-action-param="nextPreviousPage" data-live-page-param="{{page}}" class="flex items-center justify-center px-4 h-10 leading-tight font-bold text-gray-500 bg-white border border-gray-300 hover:bg-gray-100 hover:text-gray-700">{{ page }}</button>
                {% else %}
                    <span class="flex items-center justify-center px-4 h-10 text-blue-600 font-bold border border-gray-300 bg-blue-50 hover:bg-blue-100 hover:text-blue-700">{{ page }}</span>
                {% endif %}
            {% endfor %}

            {% if next is defined %}
                <button data-action="live#action" data-live-action-param="nextPreviousPage" data-live-page-param="{{next}}" class="flex items-center justify-center px-4 h-10 leading-tight font-bold text-gray-500 bg-white border border-gray-300 hover:bg-gray-100 hover:text-gray-700">&gt;</button>
            {% endif %}

            {% if last is defined and current != last %}
                <button data-action="live#action" data-live-action-param="nextPreviousPage" data-live-page-param="{{last}}" class="flex items-center justify-center px-4 h-10 leading-tight font-bold text-gray-500 bg-white border border-gray-300 rounded-e-lg hover:bg-gray-100 hover:text-gray-700">&gt;&gt;</button>
            {% endif %}
        </div>
    </div>
{% endif %}
public function __construct(private PaginatorInterface $paginator, private ClientRepository $clientRepository)
{}

#[LiveAction]
public function nextPreviousPage(#[LiveArg] int $page)
{
    $this->page = $page;
}

public function getAllData()
{
    return $this->paginator->paginate($this->clientRepository->findAll(), $this->page, 50);
}

For privacy rights, I haven't put the real code, but this is a summary of the code I made. I hope it can help and inspire some people. By the way, the paginator is KnpPaginatorBundle