This is a viewer only at the moment see the article on how this works.
To update the preview hit Ctrl-Alt-R (or ⌘-Alt-R on Mac) or Enter to refresh. The Save icon lets you save the markdown file to disk
This is a preview from the server running through my markdig pipeline
Friday, 07 November 2025
Σημείωση: Έρχεται σύντομα, απλά βάζοντας τις τελευταίες πινελιές σε αυτό. Ακολουθήστε το GitHub! .
Αυτό είναι μόνο για να σας δείξω ότι κάνω πρόοδο με αυτόν τον έλεγχο!
Μετά από μήνες εξέλιξης και πολύτιμων ανατροφοδότησης από την κοινότητα (5.5k+ downloads!), είμαι ενθουσιασμένος να ανακοινώσω ότι η βιβλιοθήκη PagingTagHelper έχει φτάσει την έκδοση 1.0.0. Αυτό δεν είναι μόνο ένα χτύπημα αριθμό έκδοσης ~ αντιπροσωπεύει μια πλήρη ωρίμανση της βιβλιοθήκης με χαρακτηριστικά που την καθιστούν κατάλληλη για εφαρμογές πραγματικού κόσμου, παραγωγής.
Αν έχετε ακολουθήσει αυτή τη σειρά, θα θυμάστε ότι ξεκινήσαμε με χωρίς κόκκαλα επιβίβασης, προστίθεται ταξινομούμενες κεφαλίδες, και εξορύσσονται Έλεγχος μεγέθους σελίδας. Έκδοση 1.0.0 παίρνει όλα όσα έχουμε μάθει και προσθέτει κρίσιμα χαρακτηριστικά της επιχείρησης:
Ας βουτήξουμε σε κάθε ένα από αυτά τα χαρακτηριστικά και να δούμε πώς λειτουργούν μαζί για να δημιουργήσουν μια πραγματικά ευέλικτη λύση pagination.
Παραδοσιακή σελιδοποίηση λειτουργεί όμορφα με SQL βάσεις δεδομένων, όπου μπορείτε εύκολα SKIP και TAKE ρεκόρ. Αλλά τι συμβαίνει όταν εργάζεστε με τις βάσεις δεδομένων NoSQL όπως Cosmos DB, DynamoDB, ή Azure Αποθήκευση Τραπέζι; Αυτές οι βάσεις δεδομένων δεν υποστηρίζουν pagination βασισμένο σε όφφσετ αντ 'αυτού, χρησιμοποιούν continuing points.
Εδώ είναι πώς η συνέχιση της συμβολικής σελιδοποίησης διαφέρει από την παραδοσιακή επιγραφή:
graph TD
A[Traditional Paging] --> B[Page 1: OFFSET 0 LIMIT 10]
A --> C[Page 2: OFFSET 10 LIMIT 10]
A --> D[Page 3: OFFSET 20 LIMIT 10]
E[Token-Based Paging] --> F[Page 1: No token]
F --> G[Returns: Data + Token_A]
G --> H[Page 2: Token_A]
H --> I[Returns: Data + Token_B]
I --> J[Page 3: Token_B]
style A stroke:#0ea5e9,stroke-width:3px
style E stroke:#ec4899,stroke-width:3px
Παραδοσιακή Τηλεειδοποίηση:
Token-Based Paging:
Η νέα <continuation-pager> tag helper κάνει την εφαρμογή συμβολική-based απλό. Πρώτα, δημιουργήστε ένα μοντέλο που εφαρμόζει IContinuationPagingModel:
public class ProductPagingViewModel : IContinuationPagingModel
{
public string? NextPageToken { get; set; }
public bool HasMoreResults { get; set; }
public int PageSize { get; set; } = 25;
public int CurrentPage { get; set; } = 1;
public Dictionary<int, string>? PageTokenHistory { get; set; }
public ViewType ViewType { get; set; } = ViewType.TailwindAndDaisy;
// Your actual data
public List<Product> Products { get; set; } = new();
}
Η διεπαφή είναι ελάχιστη αλλά ισχυρή. Ας δούμε τι κάνει κάθε ιδιοκτησία:
NextPageToken: Το σύμβολο για να ανακτήσει την επόμενη σελίδα (παρέχεται από τη βάση δεδομένων σας)HasMoreResults: Boolean που δείχνει αν υπάρχουν περισσότερες σελίδεςPageSize: Είδη ανά σελίδαCurrentPage: Εμφανίστε μόνο τον αριθμό σελίδας για UIPageTokenHistory: λεξικό χαρτογράφηση των αριθμών σελίδων σε σημεία για την πίσω πλοήγησηViewType: Ποιο πλαίσιο CSS να χρησιμοποιήσει για την απόδοσηΤώρα ας εφαρμόσουμε μια ενέργεια ελεγκτή που προσομοιώνει Cosmos DB-style pagination:
[Route("Products")]
public async Task<IActionResult> Products(
int currentPage = 1,
int pageSize = 25,
string? pageToken = null,
string? tokenHistory = null)
{
// Simulate fetching from Cosmos DB
var cosmosResults = await _cosmosService.GetProductsAsync(
pageSize: pageSize,
continuationToken: pageToken
);
// Deserialize token history for backward navigation
var history = string.IsNullOrEmpty(tokenHistory)
? new Dictionary<int, string>()
: JsonSerializer.Deserialize<Dictionary<int, string>>(tokenHistory)
?? new Dictionary<int, string>();
// Store current token in history
if (!string.IsNullOrEmpty(pageToken))
{
history[currentPage] = pageToken;
}
var viewModel = new ProductPagingViewModel
{
CurrentPage = currentPage,
PageSize = pageSize,
NextPageToken = cosmosResults.ContinuationToken,
HasMoreResults = cosmosResults.HasMoreResults,
PageTokenHistory = history,
Products = cosmosResults.Items
};
if (Request.IsHtmx())
{
return PartialView("_ProductList", viewModel);
}
return View(viewModel);
}
Αυτή η εφαρμογή δείχνει πώς η ιστορία σημάτων επιτρέπει την πίσω πλοήγηση. Χωρίς αυτήν, η συνέχιση της συμβολικής επιγραφής θα υποστήριζε μόνο τα πλήκτρα "Next" . Με τη διατήρηση ενός λεξικού των χαρτογραφήσεων page-to-to-ken, μπορούμε να υποστηρίξουμε τόσο την "Προηγούμενη" όσο και την "Επόμενη" πλοήγηση.
Εδώ είναι η ροή της συμβολικής συσσώρευσης οπτικοποιημένη:
sequenceDiagram
participant User
participant Controller
participant Database
participant TokenHistory
User->>Controller: Request Page 1 (no token)
Controller->>Database: Query with no token
Database-->>Controller: Data + Token_A
Controller->>TokenHistory: Store Token_A for page 1
Controller-->>User: Display Page 1
User->>Controller: Request Page 2 (Token_A)
Controller->>Database: Query with Token_A
Database-->>Controller: Data + Token_B
Controller->>TokenHistory: Add Token_B for page 2
Controller-->>User: Display Page 2
User->>Controller: Request Page 1 (retrieve from history)
Controller->>TokenHistory: Get Token for Page 1
Controller->>Database: Query with Token_A
Database-->>Controller: Data + Token_A
Controller-->>User: Display Page 1
Στην άποψη Razor σας, χρησιμοποιώντας τη συνέχιση βομβητής είναι απλό:
@model ProductPagingViewModel
<div id="product-container">
<table class="table">
<thead>
<tr>
<th>Product</th>
<th>Company</th>
<th>Price</th>
</tr>
</thead>
<tbody>
@foreach (var product in Model.Products)
{
<tr>
<td>@product.Name</td>
<td>@product.CompanyName</td>
<td>[email protected]("N2")</td>
</tr>
}
</tbody>
</table>
<continuation-pager
model="Model"
htmx-target="#product-container"
show-page-number="true"
show-pagesize="true" />
</div>
Ο βοηθός ετικέτας αυτόματα:
HasMoreResults είναι ψεύτικηΗ ιδιοφυΐα της προσέγγισης της ιστορίας είναι ότι είναι εντελώς προαιρετικό. Αν χρειάζεστε μόνο "Επόμενη" πλοήγηση (ατελείωτη περγαμηνή, για παράδειγμα), μπορείτε να αγνοήσετε εντελώς την ιστορία των μαρκών:
<continuation-pager
model="Model"
enable-token-accumulation="false"
show-page-number="false" />
Αυτό καθιστά μόνο ένα κουμπί "Επόμενο" χωρίς δείκτες σελίδας ή τη διαχείριση ιστορίας.
Για την πλήρη πλοήγηση, η ιστορία του σημειώματος είναι αυτόματα κατά σειρά ως JSON στη συμβολοσειρά ερώτημα.
/Products?currentPage=3&pageSize=25&pageToken=abc123&tokenHistory=%7B%221%22%3A%22xyz789%22%2C%222%22%3A%22abc123%22%7D
Η tokenHistory παράμετρος περιέχει το κωδικοποιημένο λεξικό, καθιστώντας την πίσω πλοήγηση απρόσκοπτη.
Μια από τις πιο σημαντικές βελτιώσεις UX στη συνέχιση βομβητή είναι αριθμημένα κουμπιά σελίδας. Καθώς πλοηγείστε προς τα εμπρός, ο βομβητής εμφανίζει κλικ αριθμούς σελίδων για όλες τις σελίδες που επισκέπτονται:
Initial page 1: [Next →]
After next click: [← Prev] [1] [2 active] [3 disabled] [Next →]
After next click: [← Prev] [1] [2] [3 active] [4 disabled] [Next →]
Click page 2: [← Prev] [1] [2 active] [3] [4 disabled] (no next - not visited yet)
Αυτό παρέχει παραδοσιακή pagination UX, διατηρώντας παράλληλα τη συμβολική-βασισμένη αρχιτεκτονική backend. Η εφαρμογή αποθηκεύει μάρκες για κάθε επισκεπτόμενη σελίδα, επιτρέποντας την απευθείας πλοήγηση σε οποιαδήποτε προηγουμένως προσβάσιμη σελίδα.
Περιορισμένη ανάπτυξη ιστορίας:
Για την πρόληψη της χρήσης αμόλυβδης μνήμης, set max-history-pages (προκαθορισμένο: 20):
<continuation-pager
model="Model"
max-history-pages="50"
show-page-number="true" />
Όταν επιτευχθεί το όριο, οι παλαιότερες μάρκες σελίδας κόβονται αυτόματα.
Αυτό είναι το πιο σημαντικό χαρακτηριστικό της συνέχισης της εφαρμογής βομβητή.
Τα σημεία συνέχειας ισχύουν μόνο με το ίδιο πλαίσιο ερωτημάτων (φίλτρα, είδη, αναζητήσεις) που τα δημιούργησε. Χρησιμοποιώντας ένα σημείο με διαφορετικές παραμέτρους ερώτημα θα επιστρέψει εσφαλμένα δεδομένα ή θα αποτύχει εξ ολοκλήρου.
Ο βομβητής συνεχίζει αυτόματα διατηρεί ΟΛΕΣ τις παραμέτρους ερώτημα εκτός από τις δικές του:
<!-- URL with filters -->
/Products?category=electronics&brand=acme&minPrice=100
<!-- After clicking Next -->
/Products?category=electronics&brand=acme&minPrice=100¤tPage=2&pageToken=xyz123&tokenHistory={...}
<!-- All filters preserved! Token is valid because query context matches. -->
Μπορείτε να απενεργοποιήσετε αυτή τη συμπεριφορά αν χρειαστεί:
<continuation-pager
model="Model"
preserve-query-parameters="false" />
Αλλά αυτό είναι έντονα αποθαρρυμένη Εκτός αν είσαι απόλυτα σίγουρος ότι οι μάρκες σου δεν εξαρτώνται από το περιεχόμενο των ερωτήσεων.
Γιατί Έχει Σημασία αυτό:
Παράδειγμα Cosmos DB:
// Page 1 with filter
var query = container.GetItemQueryIterator<Product>(
"SELECT * FROM c WHERE c.category = 'electronics'",
continuationToken: null
);
var response = await query.ReadNextAsync();
// Returns: Products + Token_A
// Page 2 with SAME filter - Token_A is valid
var query2 = container.GetItemQueryIterator<Product>(
"SELECT * FROM c WHERE c.category = 'electronics'",
continuationToken: Token_A // ✅ Works!
);
// Page 2 with DIFFERENT filter - Token_A is invalid
var query3 = container.GetItemQueryIterator<Product>(
"SELECT * FROM c WHERE c.category = 'computers'",
continuationToken: Token_A // ❌ Wrong results or error!
);
Η αυτόματη διατήρηση παραμέτρων του βομβητή εξασφαλίζει ότι οι μάρκες χρησιμοποιούνται πάντα με το αρχικό τους πλαίσιο ερωτήσεων.
Οι σύγχρονες εφαρμογές εξυπηρετούν το παγκόσμιο κοινό, και οι έλεγχοι σελιδοποίησης πρέπει να μιλούν τη γλώσσα των χρηστών σας. Έκδοση 1.0.0 περιλαμβάνει ολοκληρωμένη υποστήριξη εντοπισμού ενσωματωμένο στη βιβλιοθήκη.
Τα πλοία της βιβλιοθήκης με μεταφράσεις για 8 γλώσσες:
Ο κωδικός είναι η γλώσσα
|------|----------|
| en Αγγλικά (προκαθορισμένα) (προκαθορισμένα)
| de - Γερμανικά (Deutsch) - Γερμανικά (Deutsch) - Γερμανικά (Deutsch) - Γερμανικά) - Γερμανικά (Deutsch) - Γερμανικά) - Γερμανικά (Deutsch) - Γερμανικά)
| es Ισπανικά (Español)
| fr Γαλλικά (Français)
| it - Ιταλικά (Ιταλικά) - Ιταλικά - Ιταλικά - Ιταλικά - Ιταλικά - Ιταλικά - Ιταλικά - Ιταλικά - Ιταλικά - Ιταλικά - Ιταλικά - Ιταλικά
| pt Πορτογαλικά (Português)
| ja Γιαπωνέζικος γιαπωνέζικος γιαπωνέζικος γιαπωνέζικος γιαπωνέζικος γιαπωνέζικος γιαπωνέζικος γιαπωνέζος
| zh-Hans Απλοποιημένη Κινεζική () ()
Όλο το κείμενο είναι εντοπισμένο, συμπεριλαμβανομένων:
Το σύστημα εντοπισμού τροφοδοτείται από .resx αρχεία πόρων, καθιστώντας εύκολο να προσθέσετε τις δικές σας γλώσσες. mostlylucid.pagingtaghelper/Resources/.
Χρησιμοποιώντας τον εντοπισμό είναι απλή. Απλά προσθέστε το language χαρακτηριστικό:
<paging
model="Model"
language="de"
show-summary="true"
first-last-navigation="true" />
Αυτό καθιστά όλο το κείμενο στα γερμανικά:
<!-- Previous button -->
<button>‹ Vorherige</button>
<!-- Summary -->
<div class="text-sm text-gray-600">
Zeige 1 bis 10 von 256 Einträgen
</div>
<!-- Next button -->
<button>Nächste ›</button>
Για δυναμική αλλαγή γλώσσας με βάση τις προτιμήσεις των χρηστών, ορίστε τη γλώσσα στο χειριστήριό σας:
public async Task<IActionResult> Products(
int page = 1,
int pageSize = 10,
string language = "en")
{
var pagingModel = await GenerateModel(page, pageSize);
ViewBag.SelectedLanguage = language;
return View(pagingModel);
}
Στη συνέχεια, κατά την άποψή σας, δημιουργήστε έναν επιλογέα γλώσσας:
@{
var selectedLanguage = ViewBag.SelectedLanguage as string ?? "en";
var languages = new Dictionary<string, string>
{
{ "en", "English" },
{ "de", "German" },
{ "es", "Spanish" },
{ "fr", "French" },
{ "it", "Italian" },
{ "pt", "Portuguese" },
{ "ja", "Japanese" },
{ "zh-Hans", "Chinese" }
};
}
<select onchange="window.location.href='/Products?language=' + this.value">
@foreach (var lang in languages)
{
<option value="@lang.Key" selected="@(lang.Key == selectedLanguage)">
@lang.Value
</option>
}
</select>
<paging
model="Model"
language="@selectedLanguage"
link-url="/Products" />
Μπορείτε επίσης να παρακάμψετε τις μεμονωμένες συμβολοσειρές κειμένου, ενώ εξακολουθείτε να ωφελείστε από τον εντοπισμό άλλων στοιχείων:
<paging
model="Model"
language="ja"
previous-page-text="戻る"
next-page-text="次へ"
summary-template="全{TotalItems}件中 {StartItem}~{EndItem}件を表示" />
Η PagingLocalizer Εάν περάσετε έναν άκυρο γλωσσικό κώδικα, πέφτει χαριτωμένα πίσω στα αγγλικά.
Για την ενσωμάτωση HTMX, θα θέλετε να διατηρήσετε τη γλώσσα σε όλες τις αιτήσεις:
<script>
htmx.on('htmx:configRequest', function(event) {
if (event.detail.path.includes('/Products')) {
event.detail.parameters.language = '@selectedLanguage';
}
});
</script>
Αυτό εξασφαλίζει HTMX μερικές ενημερώσεις προβολής διατηρούν την επιλεγμένη γλώσσα.
Μία από τις πιο σημαντικές βελτιώσεις στο v1.0.0 είναι η εισαγωγή των ευέλικτων τρόπων JavaScript. Προηγουμένως, είχατε μια επιλογή boolean: use-htmx="true" ή use-htmx="false". Τώρα έχετε πέντε διαφορετικούς τρόπους, η κάθε βελτιστοποιημένη για διαφορετικά σενάρια.
Εδώ είναι η πλήρης ανάλυση των τρόπων JavaScript:
graph TD
A[JavaScript Modes] --> B[HTMX]
A --> C[HTMXWithAlpine]
A --> D[Alpine]
A --> E[PlainJS]
A --> F[NoJS]
B --> B1[Uses HTMX for partial updates]
B --> B2[hx-get, hx-target, hx-swap]
C --> C1[HTMX + Alpine.js directives]
C --> C2[Enhanced interactivity]
D --> D1[Pure Alpine.js]
D --> D2[x-data, @click handlers]
E --> E1[Vanilla JavaScript]
E --> E2[onclick handlers]
F --> F1[Zero JavaScript]
F --> F2[Standard anchor links & forms]
Ας δούμε κάθε λειτουργία σε δράση:
1. Λειτουργία HTMX (προεπιλογή)
<paging
model="Model"
js-mode="HTMX"
htmx-target="#results-container" />
Ενοικιαστές:
<button hx-get="/Products?page=2" hx-target="#results-container" hx-swap="outerHTML">
Next ›
</button>
Ιδανικό για δυναμικές ενημερώσεις σελίδων χωρίς επαναφορτίσεις πλήρους σελίδας. Αυτή είναι η συνιστώμενη λειτουργία για σύγχρονες εφαρμογές ASP.NET Core.
2. HTMXWith Alpine Mode
<paging
model="Model"
js-mode="HTMXWithAlpine"
htmx-target="#results-container" />
Ενοικιαστές:
<button
x-data
hx-get="/Products?page=2"
hx-target="#results-container"
hx-swap="outerHTML">
Next ›
</button>
Συνδυάζει HTMX για πλοήγηση με Alpine.js για επιπλέον διαδραστικό client-side. Χρησιμοποιήστε αυτό όταν χρειάζεστε αντιδραστικά στοιχεία UI παράλληλα με pagination (δείκτες φόρτωσης, κινούμενα σχέδια, επικύρωση client-side).
3. Αλπική λειτουργία
<paging
model="Model"
js-mode="Alpine" />
Ενοικιαστές:
<button
x-data
@click="window.location.href = '/Products?page=2'">
Next ›
</button>
Pure Alpine.js χωρίς HTMX. Χρήσιμο όταν χρησιμοποιείτε ήδη Alpine.js αλλά δεν θέλετε εξαρτήσεις HTMX.
4. Λειτουργία PlainJS
<paging
model="Model"
js-mode="PlainJS" />
Ενοικιαστές:
<button onclick="window.location.href = '/Products?page=2'">
Next ›
</button>
Δεν υπάρχουν εξαρτήσεις πλαίσιο, μόνο βανίλια JavaScript. Αυτή η λειτουργία περιλαμβάνει επίσης έναν βοηθό για αλλαγές μεγέθους σελίδας:
@Html.PageSizeOnchangeSnippet()
Αυτό εγχύει το απαραίτητο JavaScript για τον χειρισμό αλλαγών μεγέθους σελίδας πτώση χωρίς HTMX.
5. Λειτουργία NoJS
<paging
model="Model"
js-mode="NoJS" />
Ενοικιαστές:
<!-- Navigation uses standard anchor links -->
<a href="/Products?page=2">Next ›</a>
<!-- Page size uses a form with submit button -->
<form method="get" action="/Products">
<input type="hidden" name="page" value="1" />
<select name="pageSize" onchange="this.form.submit()">
<option value="10">10</option>
<option value="25" selected>25</option>
<option value="50">50</option>
</select>
<noscript>
<button type="submit">Update</button>
</noscript>
</form>
Zero JavaScript απαιτείται. Ιδανικό για:
Η ομορφιά αυτού του συστήματος είναι ότι όλες οι λειτουργίες διατηρούν τις υπάρχουσες παραμέτρους ερωτημάτων σας. Είτε φιλτράρετε ανά κατηγορία, ψάχνετε, ή ταξινομήσετε, η σελιδοποίηση διατηρεί την κατάστασή σας αυτόματα.
Για την οπισθοδρομική συμβατότητα, το παλιό use-htmx Το χαρακτηριστικό εξακολουθεί να λειτουργεί:
<!-- Old syntax (still works) -->
<paging model="Model" use-htmx="true" />
<!-- Equivalent to js-mode="HTMX" -->
<paging model="Model" use-htmx="false" />
<!-- Equivalent to js-mode="PlainJS" -->
Ωστόσο, συνιστώ να μεταναστεύσει στο νέο js-mode χαρακτηριστικό για σαφήνεια:
<!-- New syntax (recommended) -->
<paging model="Model" js-mode="HTMX" />
<paging model="Model" js-mode="PlainJS" />
Έκδοση 1.0.0 εισάγει δύο σημαντικές προσθήκες ViewType που αντιμετωπίζουν κοινά σενάρια πραγματικού κόσμου.
Προηγουμένως, αν ήθελες στυλ TailwindsS, πήρες το TailwindAndDaisy θέα που χρησιμοποιεί τα συστατικά DaisyUI. Αυτό είναι υπέροχο αν χρησιμοποιείτε ήδη DaisyUI, αλλά τι γίνεται αν θέλετε καθαρό Tailwind χωρίς την εξάρτηση DaisyUI?
Εισάγετε ViewType.Tailwind:
<paging
model="Model"
view-type="Tailwind" />
Αυτό καθιστά τη χρήση μόνο πρότυπο Tailwind κατηγορίες χρησιμότητας:
<div class="flex gap-2 items-center">
<button class="px-4 py-2 text-sm font-medium rounded-md bg-blue-600 text-white hover:bg-blue-700">
‹ Previous
</button>
<div class="px-3 py-1 text-sm font-medium bg-gray-100 dark:bg-gray-700 dark:text-white rounded-md">
Page 1
</div>
<button class="px-4 py-2 text-sm font-medium rounded-md bg-blue-600 text-white hover:bg-blue-700">
Next ›
</button>
</div>
Όχι. btn, badge, ή join Τάιλγουιν. Αυτό σας δίνει τον πλήρη έλεγχο του στυλ χωρίς εξαρτήσεις βιβλιοθήκης συστατικών.
Σύγκριση:
~ ViewType ~ CSS Framework ~ Component Library ~ Use Case ~
|----------|---------------|-------------------|----------|
| TailwindAndDaisy DaisyUI Projects already using DaisyUI
| Tailwind Κανένας καθαρός ανεμοστρόβιλος έργα
| Bootstrap Μπουτστραπ
| Plain Δεν υπάρχει κανένα πλαίσιο εξαρτήσεων
| NoJS Δεν υπάρχουν απαιτήσεις JavaScript
Η NoJS ViewType συνδυάζει μηδέν JavaScript με το απλό στυλ CSS:
<paging
model="Model"
view-type="NoJS"
show-pagesize="true" />
Βασικές διαφορές από άλλους τύπους προβολής:
<a href="/Products?page=2" class="pager-button">Next ›</a>
<form method="get" action="/Products" class="page-size-form">
<!-- Preserves all current query parameters as hidden inputs -->
<input type="hidden" name="search" value="laptop" />
<input type="hidden" name="category" value="electronics" />
<!-- Reset to page 1 when changing page size -->
<input type="hidden" name="page" value="1" />
<label for="pageSize">Items per page:</label>
<select name="pageSize" onchange="this.form.submit()">
<option value="10">10</option>
<option value="25" selected>25</option>
<option value="50">50</option>
</select>
<!-- Button visible when JavaScript is disabled -->
<noscript>
<button type="submit" class="page-size-button">Update</button>
</noscript>
</form>
Η onchange="this.form.submit()" παρέχει ευκολία όταν JavaScript είναι διαθέσιμη, αλλά το <noscript> Το κουμπί εξασφαλίζει πλήρη λειτουργικότητα όταν δεν είναι.
Μια από τις πιο απογοητευτικές πτυχές των εφαρμογών pagination είναι να χάνετε τα φίλτρα σας, τους όρους αναζήτησης, ή να ταξινομήσετε την τάξη κατά την πλοήγηση μεταξύ των σελίδων. Έκδοση 1.0.0 λύνει αυτό κομψά από αυτόματα διατηρώντας όλες τις παραμέτρους ερωτημάτων εκτός από τις παραμέτρους ελέγχου επικόλλησης.
Αυτό το χαρακτηριστικό λειτουργεί πανομοιότυπα σε όλο τόσο οι τακτικές βομβητές όσο και οι συνεχείς βομβητές, και κατά μήκος όλες τις λειτουργίες JavaScript και ViewTypes.
Εδώ είναι πώς λειτουργεί εσωτερικά:
string BuildQueryString(string? token, int page)
{
var query = new Dictionary<string, string>();
// Define continuation pager's own parameters that should be excluded from preservation
var pagerParams = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
"pageSize", "currentPage", "pageToken", "tokenHistory"
};
// Add parameter prefix variants if using prefixed parameters
if (!string.IsNullOrEmpty(Model.ParameterPrefix))
{
pagerParams.Add($"{Model.ParameterPrefix}_pageSize");
pagerParams.Add($"{Model.ParameterPrefix}_currentPage");
pagerParams.Add($"{Model.ParameterPrefix}_pageToken");
pagerParams.Add($"{Model.ParameterPrefix}_tokenHistory");
}
// Preserve all existing query parameters (except pager's own) if enabled
if (Model.PreserveQueryParameters)
{
foreach (var param in ViewContext.HttpContext.Request.Query)
{
if (!pagerParams.Contains(param.Key))
{
query[param.Key] = param.Value.ToString();
}
}
}
// Add continuation pager parameters (with prefix if specified)
var pageSizeParam = Model.GetParameterName("pageSize");
var currentPageParam = Model.GetParameterName("currentPage");
var pageTokenParam = Model.GetParameterName("pageToken");
var tokenHistoryParam = Model.GetParameterName("tokenHistory");
query[pageSizeParam] = pageSize.ToString();
query[currentPageParam] = page.ToString();
if (!string.IsNullOrEmpty(token))
query[pageTokenParam] = token;
if (Model.EnableTokenAccumulation)
query[tokenHistoryParam] = tokenHistoryJson;
return string.Join("&", query.Select(kvp =>
$"{Uri.EscapeDataString(kvp.Key)}={Uri.EscapeDataString(kvp.Value)}"));
}
Η προσέγγιση αυτή σημαίνει:
Σενάριο 1: Αναζήτηση + Pagination
Initial URL: /Products?search=laptop&category=electronics&page=1
Click Next: /Products?search=laptop&category=electronics&page=2
Change Page Size: /Products?search=laptop&category=electronics&page=1&pageSize=50
Σενάριο 2: Ταξινόμηση + Pagination
Initial URL: /Products?orderBy=price&descending=true&page=1
Click Page 3: /Products?orderBy=price&descending=true&page=3
Σενάριο 3: Συνέχισε τον καλλιτέχνη με τα φίλτρα
Initial URL: /Products?category=electronics&brand=acme
Click Next: /Products?category=electronics&brand=acme¤tPage=2&pageToken=abc123&tokenHistory={...}
Η ίδια συντήρηση λειτουργεί σε μορφές (NoJS mode). Κατά την απόδοση του μεγέθους της σελίδας μορφή, η προβολή περιλαμβάνει αυτόματα κρυφές εισόδους για όλες τις παραμέτρους εκτός πίνακα:
<form method="get" action="@linkUrl" class="page-size-form">
@* Preserve all existing query parameters except pageSize and page-related ones *@
@foreach (var param in ViewContext.HttpContext.Request.Query)
{
if (!new[] { "pageSize", "currentPage", "pageToken", "tokenHistory" }
.Contains(param.Key, StringComparer.OrdinalIgnoreCase))
{
<input type="hidden" name="@param.Key" value="@param.Value" />
}
}
@* Reset to page 1 when changing page size *@
<input type="hidden" name="currentPage" value="1" />
<select name="pageSize" onchange="this.form.submit()">
<!-- options -->
</select>
</form>
Αυτό λειτουργεί απρόσκοπτα σε όλη την όλες τις λειτουργίες JavaScript και όλα τα είδη προβολήςΠοτέ δεν χρειάζεται να διαχειριστείς χειροκίνητα την εξάπλωση των χορδών.
Η αναβάθμιση από προ-1.0 εκδόσεις είναι απλή, αλλά υπάρχουν μερικές αλλαγές που πρέπει να γνωρίζουμε.
1. use-htmx είναι depreced (αλλά εξακολουθεί να λειτουργεί)
Παλιό:
<paging model="Model" use-htmx="true" />
<paging model="Model" use-htmx="false" />
Νέο (συνιστάται):
<paging model="Model" js-mode="HTMX" />
<paging model="Model" js-mode="PlainJS" />
2. ViewType.TailwindAndDaisy χρησιμοποιεί τώρα πλήρη στοιχεία DaisyUI
Εάν χρησιμοποιείτε ViewType.TailwindAndDaisy και θέλουν καθαρό Tailwind χωρίς DaisyUI:
Παλιά συμπεριφορά (καθαρή Tailwind):
<paging model="Model" view-type="TailwindAndDaisy" />
Νέα (για να πάρει παλιά συμπεριφορά):
<paging model="Model" view-type="Tailwind" />
Συνεχίστε να χρησιμοποιείτε TailwindAndDaisy εάν χρησιμοποιείτε συστατικά DaisyUI:
<paging model="Model" view-type="TailwindAndDaisy" />
<!-- Uses btn, join, badge, select, etc. -->
3. HTMX αναβαθμίστηκε στο 2.0.4
Εάν χρησιμοποιείτε HTMX αλλού στην εφαρμογή σας, εξασφαλίστε συμβατότητα με HTMX 2.0.4. Οι περισσότεροι HTMX 1.x κωδικός λειτουργεί αμετάβλητη, αλλά ελέγξτε το Οδηγός μετανάστευσης HTMX 2.0 για ακραίες περιπτώσεις.
Βήμα 1: Ενημέρωση του NuGet πακέτου
dotnet add package mostlylucid.pagingtaghelper --version 1.0.0
Βήμα 2: Αναθεώρηση του υπάρχοντος κώδικα σας
Αναζήτηση βάσης κώδικα σας για use-htmx χαρακτηριστικά:
# PowerShell
Get-ChildItem -Recurse -Include *.cshtml | Select-String "use-htmx"
# Bash/Git Bash
grep -r "use-htmx" --include="*.cshtml" .
Βήμα 3: Ενημέρωση σε κατάσταση js (συνιστάται)
Αντικατάσταση use-htmx με js-mode:
- <paging model="Model" use-htmx="true" htmx-target="#results" />
+ <paging model="Model" js-mode="HTMX" htmx-target="#results" />
- <paging model="Model" use-htmx="false" />
+ <paging model="Model" js-mode="PlainJS" />
Βήμα 4: κριτική TailwindAndDaisy χρήση
Αν δεν έχετε εγκαταστήσει το DaisyUI αλλά χρησιμοποιούσαν TailwindAndDaisy:
- <paging model="Model" view-type="TailwindAndDaisy" />
+ <paging model="Model" view-type="Tailwind" />
Βήμα 5: Δοκιμάστε σχολαστικά
Εκτελέστε την εφαρμογή και τη δοκιμή σας:
Μόλις μεταναστεύσει, σκεφτείτε την υιοθέτηση αυτών των νέων χαρακτηριστικών:
Τοπικοποίηση:
<paging
model="Model"
language="@CultureInfo.CurrentUICulture.TwoLetterISOLanguageName" />
Συνέχισε τον πομπό (εάν χρησιμοποιείς το NoSQL):
<continuation-pager
model="Model"
htmx-target="#results-container"
show-page-number="true" />
Λειτουργία NoJS (για προσβασιμότητα):
<paging model="Model" js-mode="NoJS" />
Η βιβλιοθήκη περιλαμβάνει μια ολοκληρωμένη demo εφαρμογή επιδεικνύοντας όλα τα χαρακτηριστικά γνωρίσματα. Μπορείτε να το τρέξετε τοπικά ή να το δείτε στο site demo (Έρχεται σύντομα).
Τρέχει το Demo τοπικά:
git clone https://github.com/scottgal/mostlylucid.pagingtaghelper.git
cd mostlylucid.pagingtaghelper/mostlylucid.pagingtaghelper.sample
dotnet run
Πλοηγηθείτε στο https://localhost:5001 για να εξερευνήσετε:
Κάθε demo περιλαμβάνει:
Έκδοση 1.0.0 αντιπροσωπεύει ένα σημαντικό ορόσημο για τη βιβλιοθήκη PagingTagHelper. Τι ξεκίνησε ως μια απλή απαίτηση εργασίας έχει εξελιχθεί σε μια ολοκληρωμένη, έτοιμη για την παραγωγή λύση σελιδοποίησης που χειρίζεται:
Η βιβλιοθήκη έχει δοκιμαστεί με λήψεις 1.7k+ και είναι έτοιμη για παραγωγή. Και οι 106 δοκιμές μονάδων περνούν, και η ολοκληρωμένη εφαρμογή demo παρουσιάζει πρότυπα χρήσης πραγματικού κόσμου.
Μελλοντικές βελτιώσεις Σκέφτομαι:
Εγκατάσταση μέσω NuGet:
dotnet add package mostlylucid.pagingtaghelper --version 1.0.0
Ελέγξτε τα έγγραφα:
Ερωτήσεις, σχόλια ή συνεισφορές; Ανοίξτε ένα θέμα για το GitHub ή επικοινωνήστε στο Twitter @scottgal.
Καλή περιπέτεια!
© 2026 Scott Galloway — Unlicense — All content and source code on this site is free to use, copy, modify, and sell.