Support Wire Drupal
Fund development, features & content
Pagination
Implement pagination in components
WireDrupal offers the ability to paginate results within a component.
Unfortunately, Drupal's existing pagination implementation is not flexible enough to be extended but a helper trait
\Drupal\wire\WithPagination
is available to help with the implementation.
Paginating Data
Assuming you have a articles
component, but you want to limit the results to 10 articles per page.
Here is an example of a simple prev/next pager implementation.
use Drupal\wire\Plugin\Attribute\WireComponent;
use Drupal\wire\WireComponentBase;
use Drupal\wire\View;
use Drupal\wire\WithPagination;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
#[WireComponent(id: 'articles')]
class Articles extends WireComponentBase {
use WithPagination;
const ITEMS_PER_PAGE = 10;
public string $search = '';
protected string $pagerId = 'page';
protected array $queryString = [
'search' => ['except' => '', 'as' => 's'],
'page' => ['except' => 0, 'as' => 'p'],
];
protected ?EntityTypeManagerInterface $entityTypeManager;
public static function create(
ContainerInterface $container,
array $configuration,
$plugin_id,
$plugin_definition
) {
$instance = new static($configuration, $plugin_id, $plugin_definition);
$instance->entityTypeManager = $container->get('entity_type.manager');
return $instance;
}
public function render(): ?View {
$nodeStorage = $this->entityTypeManager->getStorage('node');
$nodeQuery = $nodeStorage->getQuery()->accessCheck(TRUE);
$getBaseQuery = function () use ($nodeQuery) {
$thisQuery = clone $nodeQuery;
$query = $thisQuery
->condition('type', 'article')
->condition('status', 1);
if (!empty($this->search)) {
$query->condition('title', '%' . addcslashes($this->search, '\%_') . '%', 'LIKE');
}
return $query;
};
$articleEntities = $nodeStorage->loadMultiple(
$getBaseQuery()
->range(floor($this->page * self::ITEMS_PER_PAGE), self::ITEMS_PER_PAGE)
->execute()
);
$totalPages = ceil($getBaseQuery()->count()->execute() / self::ITEMS_PER_PAGE);
return View::fromTpl('articles', [
'items' => array_map(function ($article) {
return $article->label();
}, $articleEntities),
'paginator' => $this->getPaginationData($totalPages),
]);
}
private function getPaginationData($totalPages): ?array {
$items = [];
if ($this->page > 0) {
$items['previous']['hasItems'] = TRUE;
}
if ($this->page < ($totalPages - 1)) {
$items['next']['hasItems'] = TRUE;
}
return ['items' => $items];
}
}
<div>
<input
wire:model="search"
type="search"
placeholder="Search article by title..."
>
<h1>Articles</h1>
<ul>
{% for item in items %}
<li>{{ item }}</li>
{% endfor %}
</ul>
{% if paginator.items %}
<nav role="navigation" aria-labelledby="pager-articles">
{% if paginator.items.previous.hasItems %}
<button
wire:click="previousPage()"
wire:loading.attr="disabled"
type="button"
title="{{ 'Go to previous page'|t }}"
>
{{ 'Prev'|t }}
</button>
{% endif %}
{% if paginator.items.next.hasItems %}
<button
wire:click="nextPage()"
wire:loading.attr="disabled"
type="button"
title="{{ 'Go to next page'|t }}"
>
{{ 'Next'|t }}
</button>
{% endif %}
</nav>
{% endif %}
</div>
Note that the
$pagerId
property must be defined in order to identify the pager in case there are multiple ones on the same page.The
previousPage()
andnextPage()
methods are provided byWithPagination
trait which callssetPage()
method. You should call this method if you're implementing a Full pagination to go to a specific page.
Resetting Pagination
You might need to reset pagination to it's first page when performing any filtering of data as it would return a different set of results.
E.g: If a user is on the page "2" and types into a search field to narrow the results you might want to reset the page as first one to make sense of any pagination if any.
The WithPagination
trait exposes a resetPage()
method to accomplish this.
This method can be used in combination with the updating/updated
lifecycle hooks to reset the page when certain
component data is updated.
public function updatingSearch(): void {
$this->resetPage();
}
If you implemented the search when a specific action, add $this->resetPage();
in the action method accordingly.