TYPO3 9:如何为widget.paginate设置筛选器及AJAX下一页链接?
Hey there, let's tackle your two issues with the widget.paginate component in TYPO3 9 — keeping filter parameters across pages and getting AJAX pagination working. I've dealt with both of these before, so here's what works:
1. Stop Losing Filter Parameters When Paging
The root cause here is that your filter form uses POST, but the paginate widget generates GET links by default. POST parameters don't automatically carry over to new GET requests, so your filter gets dropped when you switch pages. Here are two reliable fixes:
Option 1: Switch to GET for the Filter Form (Quickest Fix)
If you don't mind filter params showing up in the URL, changing your form to use GET will let the paginate widget automatically pick up and retain those values. Update your form and paginate config like this:
<f:form name="filter" controller="myController" pluginName="Plugin" action="list" method="GET"> <f:if condition="{types}"> <f:for each="{types}" as="type"> <f:form.checkbox name="filter[types][]" value="{type.uid}" checked="{filter.types is not empty and filter.types contains type.uid}" /> <label>{type.title}</label> </f:for> </f:if> <f:form.submit value="Apply Filter" /> </f:form> <f:widget.paginate objects="{myObject}" as="Objects" configuration="{ itemsPerPage: 20, insertAbove: 1, insertBelow: 1, maximumNumberOfLinks: 10, addQueryString: 1 }" > <!-- Your list output here --> <f:for each="{Objects}" as="item"> <div class="list-item">{item.title}</div> </f:for> </f:widget.paginate>
The addQueryString: 1 setting tells the widget to append all current GET parameters (including your filter) to every pagination link — problem solved.
Option 2: Store Filter in Session (For POST-Based Forms)
If you need to stick with POST (e.g., for sensitive data or cleaner URLs), save the filter values to the user's session so they persist across page loads. Here's how to implement this:
Update Your Controller's list Action
use TYPO3\CMS\Core\Session\SessionInterface; use TYPO3\CMS\Core\Database\Connection; use TYPO3\CMS\Core\Database\Query\QueryBuilder; /** * @param array $filter * @return string */ public function listAction(array $filter = []) { /** @var SessionInterface $session */ $session = $this->request->getSession(); // Update session filter if user submits new filters if (!empty($filter)) { $session->set('myPluginFilter', $filter); } else { // Fallback to saved filter if no new submission $filter = $session->get('myPluginFilter') ?? []; } // Reset filter if user clicks reset button if ($this->request->hasArgument('resetFilter')) { $session->remove('myPluginFilter'); $filter = []; } // Apply filter to your database query (example with QueryBuilder) $queryBuilder = $this->objectManager->get(QueryBuilder::class); $myObjectQuery = $queryBuilder ->select('*') ->from('tx_myextension_domain_model_myobject'); if (!empty($filter['types'])) { $myObjectQuery->andWhere('type IN (:types)') ->setParameter('types', $filter['types'], Connection::PARAM_INT_ARRAY); } $myObject = $myObjectQuery->execute()->fetchAll(); $this->view->assignMultiple([ 'myObject' => $myObject, 'filter' => $filter, 'types' => $this->typeRepository->findAll() ]); }
Add a reset button to your template to clear the session filter:
<f:form action="list" method="POST"> <f:form.hidden name="resetFilter" value="1" /> <f:form.submit value="Reset Filter" class="reset-btn" /> </f:form>
2. Implement AJAX Pagination (Fixing f:widget.link Issues)
The f:widget.link problem you're seeing is a known limitation in TYPO3 9's paginate widget. To get AJAX pagination working, you'll need to customize the widget's template and add some JavaScript:
Step 1: Override the Paginate Widget Template
Copy the core paginate template from typo3/sysext/fluid/Resources/Private/Partials/Widget/Paginate.html to your extension's Resources/Private/Partials/Widget/Paginate.html.
Step 2: Modify the Template for AJAX Links
Update the link sections to add an AJAX class and store the page number in a data attribute:
<!-- Modified snippet from Paginate.html --> <ul class="f3-widget-paginator"> <f:if condition="{configuration.insertAbove}"> <f:then> <f:for each="{pages}" as="page"> <li class="{page.class}"> <f:if condition="{page.isCurrent}"> <f:then><span class="current-page">{page.number}</span></f:then> <f:else> <a href="#" class="ajax-paginate-link" data-page="{page.number}"> {page.number} </a> </f:else> </f:if> </li> </f:for> </f:then> </f:if> </ul>
Step 3: Add AJAX Handling JavaScript
Create a JS file in your extension (e.g., Resources/Public/JS/ajax-pagination.js) with this code:
document.addEventListener('DOMContentLoaded', initAjaxPagination); function initAjaxPagination() { const paginationLinks = document.querySelectorAll('.ajax-paginate-link'); const listContainer = document.querySelector('#my-list-container'); // Replace with your container ID paginationLinks.forEach(link => { link.addEventListener('click', handlePaginationClick); }); } function handlePaginationClick(e) { e.preventDefault(); const targetPage = this.dataset.page; const filterForm = document.querySelector('form[name="filter"]'); const formData = new FormData(filterForm); // Add the target page to the form data formData.set('currentPage', targetPage); // Send AJAX request fetch(filterForm.action, { method: 'POST', body: formData, headers: { 'X-Requested-With': 'XMLHttpRequest' } }) .then(response => response.text()) .then(html => { // Replace the list container with the new content listContainer.innerHTML = html; // Reattach event listeners to the new pagination links initAjaxPagination(); }) .catch(error => console.error('AJAX pagination error:', error)); }
Don't forget to include this JS file in your extension's frontend setup (via TypoScript or your template).
Step 4: Update Controller to Handle AJAX Requests
Modify your list action to return only the list and pagination content when it's an AJAX request:
public function listAction(array $filter = []) { // ... (existing filter and query logic) $isAjax = $this->request->isXmlHttpRequest(); if ($isAjax) { // Render only the list + pagination partial return $this->view->renderStandaloneTemplate( 'MyController/AjaxList', [ 'myObject' => $myObject, 'filter' => $filter, 'types' => $this->typeRepository->findAll() ] ); } // Normal full-page render for non-AJAX requests return $this->view->render(); }
Create an AjaxList.html template in Resources/Private/Templates/MyController/ with just the list and paginate widget:
<div id="my-list-container"> <f:widget.paginate objects="{myObject}" as="Objects" configuration="{ itemsPerPage: 20, insertAbove: 1, insertBelow: 1, maximumNumberOfLinks: 10, addQueryString: 0 // We're handling params via AJAX now }" > <!-- Your list output here --> <f:for each="{Objects}" as="item"> <div class="list-item">{item.title}</div> </f:for> </f:widget.paginate> </div>
That should resolve both your issues — filters will persist across pages, and pagination will work via AJAX without relying on f:widget.link.
内容的提问来源于stack exchange,提问作者Chris

