支持 Laravel.io 的持续发展 →

使用livewire和客户端分页和数据累加对分组行进行分页

2023年1月13日 阅读时间:7分钟

跨越页面的分组行分页在服务器端分页中可能变得复杂。另一方面,客户端分页在页面移动方面提供简单逻辑,但在处理整个数据集方面存在瓶颈。

使用Livewire存储订单中,我们探讨了客户端分页中的“存储”或“数据累加”方法,该方法使用Livewire轻松实现!

我们不需要等待整个数据集下载完毕,我们的表会先收到一个初始数据集(含下一页权限)并定期更新,在后台轮询获取批次并累加其数据。

分页是在累加的数据上进行的,因此移动表中页面之间没有服务器响应延迟,并且当显示分组中的行时也不会有复杂性!

此客户端分页+数据累加方法是通过以下方式实现的:

  1. 客户端分页逻辑
  2. Livewire:poll在后台静默获取新数据批次
  3. Livewire的事件机制在接收到每个轮询返回的数据批次时更新表格的累加数据

继续学习

您可以在此处查看我们的完整代码库作为参考资料:https://github.com/KTanAug21/hoard-table-data-using-livewire。请确保运行迁移!

如果您选择放弃仓库途径,您需要一个同时配置了 Laravel项目LivewireTailwind 的项目。确保您有一个可分组的数据集,这样您就可以看到我们保持跨页面组内数据顺序的方法的神奇之处。运行 php artisan make:livewire article-table 命令创建您的 Livewire 组件,然后您可以自由地深入到下面的代码中。

Livewire 数据累积

使用 Livewire,我们可以轻松地设置一个具有公共属性(用于跟踪项列表)的表组件,在积累数据的同时保持它们的顺序。

然后,我们可以简单地依靠显示特定于页面的索引,在页面间移动时无需担心分组逻辑。

以下,我们将设置我们的 Livewire 控制器的公共属性和功能,以检索并累积排序后的数据。然后在最后一步,我们将更新我们的 Livewire 视图以使用事件和轮询显示和刷新累积数据。

控制器

让我们对我们的 /app/http/Livewire/ArticleTable.php 做一些修改。

# /App/Http/Livewire/ArticleTable.php

class ArticleTable extends Component
{

    // List that accumulates data over time
    public $dataRows;
    
    // This is total number of data rows for reference, 
    // Also used as a reference to stop polling once reached
    public $totalRows;

    // Used for querying next batch of data to retrieve
    public $pagination;
    public $lastNsId;

    // Override this to initialize our table 
    public function mount()
    {
        $this->pagination = 10;
        $this->initializeData();
    }

轮询累积

为了使用户体验更加流畅,我们将设置一个客户端分页表,允许无延迟的表格交互。

我们最初将查询必要的行以适应客户端表格中的第一页——带有一点点的余地。这将是一个足够快速的查询,可以在一秒内从数据库中检索,并且返回的数据量不应该太大,以免在页面加载时造成瓶颈。

这种初始数据检索的特殊之处在于它提供了比初始显示更多的行数。我们得到了双倍的第一页显示,因此有足够的数据为下一页交互提供余地。

/**
 * Initially let's get double the first page data 
 * to have a smooth next page feel 
 * while we wait for the first poll result
 */
public function initializeData()
{
    $noneSubList = $this->getBaseQuery()
    ->limit($this->pagination*2)
    ->get();

    $this->addListToData( $noneSubList );
}

/**
 * Gets the base query
 */
public function getBaseQuery()
{
    // Quickly refresh the $totalRows every time we check with the db
    $this->totalRows = Article::count();

    // Return none-Sub rows to avoid duplicates in our $dataRows list
    return Article::whereNull('lead_article_id');
}

然后,为了将更多数据放入表格中,前端可以通过轮询控制器的公共函数 nextPageData 来静默地向表格中添加条目。

/**
 * For every next page, 
 * we'll get data after our last reference
 */
public function nextPageData()
{
  $noneSubList = $this->getBaseQuery()
    ->where('id','>',$this->lastNsId)
    ->limit($this->pagination*10)
    ->get();
  
  $this->addListToData( $noneSubList );
}

加入我们的核心功能来排序我们检索的数据:获取数据结果的可能子行,将它们的子行以及数据以适当顺序合并到我们的 $dataRows 中。

 /**
 * 1. Get possible Sub rows for the list of data retrieved in nextPageData or initializeData
 * 2. Merge list of data inclusive of their possible Sub rows, in proper ordering, to the accumulated $dataRows 
 * 3. Update the $lastNsId reference for our nextPage functionality 
 */
public function addListToData($noneSubList)
{
    $subList = $this->getSubRows($noneSubList);
    foreach( $noneSubList as $item ){
        $this->dataRows[] = $item;
        $this->lastNsId   = $item->id;
        foreach( $subList as $subItem){
            if( $subItem->lead_article_id == $item->id ){
                $this->dataRows[] = $subItem;
            }
        }
    }
}


/**
 * Get the Sub rows for the given none-Sub list
 */
private function getSubRows($noneSubList)
{
    $idList = [];
    foreach($noneSubList as $item){
        $idList[] = $item->id;
    }

    return Article::whereIn('lead_article_id', $idList)->get();
}

视图

一旦我们设置了 Livewire 控制器,让我们在我们的 Livewire 组件视图 /app/resources/views/livewire/article-table.blade.php 中增加一些色彩。

<table>
  <thead>...</thead> 
  {{-- wire:ignore helps to not reload this tbody for every update done on our $dataRows --}}
  <tbody id="tbody" wire:ignore></tbody>
</table>
<nav role="navigation" aria-label="Pagination Navigation" class="flex justify-between" >
    <button onclick="prevPage()">Prev</button>
    <button onclick="nextPage()">Next</button>
</nav>

我们的设置很酷的一个地方是分页完全在客户端进行。这意味着只有用户界面能够访问到的数据才会进行交互——这意味着从 JavaScript 方面来说—— consequently,为我们用户提供了无延迟的分页体验!

客户端分页

为了显示我们的数据,我们从 JavaScript 端初始化我们需要的所有变量。这包括对表格元素的引用,一些默认的分页详细信息,以及最后的 myData 变量,可以方便地访问从 $dataRows 中接收到的数据。

将一个 <script> 部分添加到我们的 Livewire 组件视图 /app/resources/views/livewire/article-table.blade.php 中。

<script>
  // Reference to table element
  var mTable   = document.getElementById("myTable");
  // Transfer $dataRows to a JavaScript variable for easy use
  var myData   = JSON.parse('<?php echo json_encode($dataRows) ?>');
  // Default page for our users
  var page     = 1;
  var startRow = 0;
  // Let's update our table element with data
  refreshPage();

然后设置一个快速 JavaScript 函数,根据当前的 page 显示 myData 行。

function refreshPage()
{
    // Let's clear some air 
    document.getElementById("tbody").innerHTML = '';
    
    // Determine which index/row to start the page with
    startRow = calculatePageStartRow(page);
   
    // Add rows to the tbody
    for(let row=startRow; row<myData.length && row<startRow+10; row++){
        let item = myData[row];
        var rowTable = mTable.getElementsByTagName('tbody')[0].insertRow(-1);
        
        // Coloring scheme to differentiate Sub rows
        if(item['lead_article_id']!=null){
            rowTable.className = "pl-10 bg-gray-200";
            var className = "pl-10";   
        }else
            var className = ""; 

        var cell1 = rowTable.insertCell(0);
        var cell2 = rowTable.insertCell(1);
        var cell3 = rowTable.insertCell(2);
        var cell4 = rowTable.insertCell(3);

        cell1.innerHTML = '<div class="py-3 '+className+' px-6 flex items-center">' + item['url'] + '</div>';
        cell2.innerHTML = '<div class="py-3 '+className+' px-6 flex items-center">' + item['source'] + '</div>';
        cell3.innerHTML = '<div class="py-3 '+className+' px-6 flex items-center">' + item['id'] + '</div>';
        cell4.innerHTML = '<div class="py-3 '+className+' px-6 flex items-center">' + item['lead_article_id'] + '</div>';
    }
}

附带允许用户从一个页导航到下一个页和回退的功能

function nextPage()
{   
    if( calculatePageStartRow( page+1 ) < myData.length ){
        page = page+1;
        refreshPage();
    }
}

function prevPage()
{
    if( page > 1 ){
        page = page-1;
        refreshPage();
    }
}

function calculatePageStartRow( mPage )
{
    return (mPage*10)-10;
}

利用 Livewire 的神奇轮询功能,在不超越最大行数的情况下悄悄地累积剩余的数据

@if( count($dataRows) < $totalRows )
    <div wire:poll.5s>
        Loading more data... 
        {{ $this->nextPageData() }}
        {{ $this->dispatchBrowserEvent('data-updated', ['newData' => $dataRows]); }}
    </div>
@endif

最后,针对上面的dispatchBrowserEvent,让我们创建一个JavaScript监听器来刷新我们的myData列表并重新渲染表格行——以防当前页面仍有可用于显示行的空闲槽位。

window.addEventListener('data-updated', event => {
   myData = event.detail.newData;
   refreshPage();
});
 

这就完成了!我们完成了,逻辑行数不到300行!


当然,总是有改进的空间,永远不要认为没有!所以查看我的关于数据累积技巧的文章"离负载数据",以及一个使用仓库演示应用快速改进的Fly.io

上次更新1年前。

drisvints喜欢这篇文章

1
喜欢这篇文章吗?让作者知道并鼓励他们!

你可能还喜欢以下文章

2024年3月11日

如何使用Larastan从0到9搭建您的Laravel应用

Larastan,在Laravel应用执行前就发现错误是可能的,它...

阅读文章
2024年7月19日

无需特性标准化API响应

我发现,大多数创建用于API响应的库都是使用特性实现的,并且...

阅读文章
2024年7月17日

通过Discord通知收集您的Laravel项目反馈

如何在Laravel项目中创建反馈模块,并在接收到消息时收到Discord通知...

阅读文章

我们想要感谢这些 极棒的公司 为我们提供支持

您的标志在哪里?

Laravel.io

Laravel问题解决、知识分享和社区建设的门户。

© 2024 Laravel.io - 版权所有。