Typo3 9.5.8指定文件夹记录分页加载及加载更多实现问询
Hey Michael, let's walk through exactly how to build that "Load more" feature for your TYPO3 9.5.8 custom extension—AJAX doesn't have to feel intimidating, we'll break it into simple, actionable steps.
1. Backend Setup (TYPO3 Extension Logic)
First, we need to handle pagination and AJAX-friendly data fetching in your extension's controller:
- Create two controller actions:
- A
listActionfor the initial page load: This will fetch the first batch of records (e.g., 10) using TYPO3'sQueryBuilder. Calculate the total number of records too, and pass both the initial records and total count to your Fluid template.
Example snippet forlistAction:public function listAction() { $limit = 10; $offset = 0; // Fetch initial records $queryBuilder = $this->objectManager->get(QueryBuilder::class); $records = $queryBuilder ->select('*') ->from('tx_yourextension_domain_model_record') ->setMaxResults($limit) ->setFirstResult($offset) ->execute() ->fetchAllAssociative(); // Get total record count $totalRecords = $queryBuilder ->count('uid') ->from('tx_yourextension_domain_model_record') ->execute() ->fetchColumn(0); $this->view->assignMultiple([ 'records' => $records, 'totalRecords' => $totalRecords, 'currentOffset' => $limit, 'loadLimit' => $limit ]); } - A
loadMoreActionfor AJAX requests: This action will acceptoffsetandlimitparameters, fetch the next batch of records, and return only the HTML snippet for those records (no full page layout). Disable the template layout here to avoid wrapping the output with your site's header/footer.
Example snippet forloadMoreAction:public function loadMoreAction(int $offset, int $limit) { // Fetch next batch of records $queryBuilder = $this->objectManager->get(QueryBuilder::class); $records = $queryBuilder ->select('*') ->from('tx_yourextension_domain_model_record') ->setMaxResults($limit) ->setFirstResult($offset) ->execute() ->fetchAllAssociative(); // Render only the record item template (no layout) $this->view->setLayoutRootPaths([]); $this->view->assign('records', $records); return $this->view->render('Record/ItemList'); }
- A
- Register AJAX route: To let TYPO3 handle the AJAX request, add an entry in your
ext_localconf.phpto map an eID to your controller action:$GLOBALS['TYPO3_CONF_VARS']['FE']['eID_include']['your_extension_loadmore'] = \Vendor\YourExtension\Controller\RecordController::class . '::loadMoreAction';
2. Frontend Template (Fluid)
Set up your list template to render initial records and hold the "Load more" UI elements:
- Render initial records: Create a container for your records, and loop through the initial batch:
<div id="records-container"> <f:for each="{records}" as="record"> <!-- Your record HTML here, e.g.: --> <div class="record-item"> <h3>{record.title}</h3> <p>{record.description}</p> </div> </f:for> </div> - Add Load More UI: Include hidden fields to track state, a loading indicator, the button, and a "no more records" message:
<input type="hidden" id="current-offset" value="{currentOffset}"> <input type="hidden" id="total-records" value="{totalRecords}"> <input type="hidden" id="load-limit" value="{loadLimit}"> <div id="loading-status" style="display: none;">Loading more records...</div> <button id="load-more-btn" class="btn btn-primary" {f:if(condition: currentOffset >= totalRecords, then: 'style="display:none;"')}> Load more </button> <div id="no-more-records" style="display: none;">No more records to display.</div> - Create a partial for record items: Make a
Record/ItemList.htmlpartial that only renders the record items (this is what yourloadMoreActionwill return):<f:for each="{records}" as="record"> <div class="record-item"> <h3>{record.title}</h3> <p>{record.description}</p> </div> </f:for>
3. AJAX Handling (JavaScript)
Add a script to handle the button click, fetch new records, and update the DOM:
document.addEventListener('DOMContentLoaded', function() { const loadBtn = document.getElementById('load-more-btn'); const recordsContainer = document.getElementById('records-container'); const currentOffsetInput = document.getElementById('current-offset'); const totalRecordsInput = document.getElementById('total-records'); const loadLimitInput = document.getElementById('load-limit'); const loadingStatus = document.getElementById('loading-status'); const noMoreMsg = document.getElementById('no-more-records'); loadBtn?.addEventListener('click', function() { const currentOffset = parseInt(currentOffsetInput.value); const totalRecords = parseInt(totalRecordsInput.value); const loadLimit = parseInt(loadLimitInput.value); // Exit if all records are loaded if (currentOffset >= totalRecords) { noMoreMsg.style.display = 'block'; loadBtn.style.display = 'none'; return; } // Disable button and show loading state loadBtn.disabled = true; loadingStatus.style.display = 'block'; // Fetch new records via AJAX fetch(`/index.php?eID=your_extension_loadmore&offset=${currentOffset}&limit=${loadLimit}`) .then(response => { if (!response.ok) throw new Error('Request failed'); return response.text(); }) .then(html => { // Append new records to the container recordsContainer.insertAdjacentHTML('beforeend', html); // Update offset currentOffsetInput.value = currentOffset + loadLimit; // Reset UI state loadBtn.disabled = false; loadingStatus.style.display = 'none'; // Hide button if all records are loaded if (currentOffset + loadLimit >= totalRecords) { loadBtn.style.display = 'none'; noMoreMsg.style.display = 'block'; } }) .catch(error => { console.error('Error loading records:', error); loadBtn.disabled = false; loadingStatus.style.display = 'none'; }); }); });
4. Critical TYPO3-Specific Notes
- CSRF Protection: For security, add a CSRF token to your AJAX request. In your Fluid template, add
<f:form.csrfToken as="csrfToken" />, then include it in the fetch headers:fetch(`/index.php?eID=your_extension_loadmore&offset=${currentOffset}&limit=${loadLimit}`, { headers: { 'X-TYPO3-CSRF-Token': '{csrfToken}' } }) - Template Rendering: Ensure your
loadMoreActiondoesn't load the full site layout. UsingsetLayoutRootPaths([])in the action prevents TYPO3 from wrapping the output with your default layout. - Performance: Use
QueryBuilderefficiently—only select the fields you need instead of*, and consider caching the total record count if your data doesn't update frequently.
内容的提问来源于stack exchange,提问作者Michael
相关产品推荐
相关产品推荐

