PagingTagHelper v1.0.0: Enterprise-Ready Pagination for Modern ASP.NET Core (ελληνικά (Greek))

PagingTagHelper v1.0.0: Enterprise-Ready Pagination for Modern ASP.NET Core

Friday, 07 November 2025

//

21 minute read

Σημείωση: Έρχεται σύντομα, απλά βάζοντας τις τελευταίες πινελιές σε αυτό. Ακολουθήστε το GitHub! .

Αυτό είναι μόνο για να σας δείξω ότι κάνω πρόοδο με αυτόν τον έλεγχο!

Εισαγωγή

Μετά από μήνες εξέλιξης και πολύτιμων ανατροφοδότησης από την κοινότητα (5.5k+ downloads!), είμαι ενθουσιασμένος να ανακοινώσω ότι η βιβλιοθήκη PagingTagHelper έχει φτάσει την έκδοση 1.0.0. Αυτό δεν είναι μόνο ένα χτύπημα αριθμό έκδοσης ~ αντιπροσωπεύει μια πλήρη ωρίμανση της βιβλιοθήκης με χαρακτηριστικά που την καθιστούν κατάλληλη για εφαρμογές πραγματικού κόσμου, παραγωγής.

Αν έχετε ακολουθήσει αυτή τη σειρά, θα θυμάστε ότι ξεκινήσαμε με χωρίς κόκκαλα επιβίβασης, προστίθεται ταξινομούμενες κεφαλίδες, και εξορύσσονται Έλεγχος μεγέθους σελίδας. Έκδοση 1.0.0 παίρνει όλα όσα έχουμε μάθει και προσθέτει κρίσιμα χαρακτηριστικά της επιχείρησης:

  • Συνεχίστε Token Pagination για τις βάσεις δεδομένων NoSQL (Cosmos DB, DynamoDB, Azure Table Storage)
  • Πολυγλωσσική τοπικοποίηση υποστήριξη 8 γλωσσών εκτός πλαισίου
  • Ευέλικτες λειτουργίες JavaScript από HTMX σε μηδέν-JavaScript
  • Καθαρές όψεις ανέμων χωρίς εξαρτήσεις DaisyUI
  • Έξυπνη διατήρηση παραμέτρων URL σε όλη την πλοήγηση
  • HTMX 2. 04 αναβάθμιση με οπισθοδρομική συμβατότητα

Ας βουτήξουμε σε κάθε ένα από αυτά τα χαρακτηριστικά και να δούμε πώς λειτουργούν μαζί για να δημιουργήσουν μια πραγματικά ευέλικτη λύση pagination.

NuGetCity name (optional, probably does not need a translation) NuGetCity name (optional, probably does not need a translation)

Παραδοσιακή σελιδοποίηση λειτουργεί όμορφα με SQL βάσεις δεδομένων, όπου μπορείτε εύκολα SKIP και TAKE ρεκόρ. Αλλά τι συμβαίνει όταν εργάζεστε με τις βάσεις δεδομένων NoSQL όπως Cosmos DB, DynamoDB, ή Azure Αποθήκευση Τραπέζι; Αυτές οι βάσεις δεδομένων δεν υποστηρίζουν pagination βασισμένο σε όφφσετ αντ 'αυτού, χρησιμοποιούν continuing points.

Κατανόηση του Token- Based Pagination

Εδώ είναι πώς η συνέχιση της συμβολικής σελιδοποίησης διαφέρει από την παραδοσιακή επιγραφή:

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

Παραδοσιακή Τηλεειδοποίηση:

  • Μπορείτε να προσδιορίσετε ακριβώς ποια αρχεία για την ανάκτηση (OFFSET/LIMIT)
  • Μπορείτε να πηδήξετε σε οποιαδήποτε σελίδα άμεσα
  • Η βάση δεδομένων πρέπει να σαρώσει όλα τα προηγούμενα αρχεία

Token-Based Paging:

  • Βάση δεδομένων επιστρέφει ένα αδιαφανές σημείο που αντιπροσωπεύει "πού να συνεχίσει"
  • Token μορφή είναι ειδική βάση δεδομένων και αδιαφανές για τον πελάτη
  • Forward navigation is natural, backing navigation requires marking history

Εφαρμογή χρονοδιακόπτη συνέχειας

Η νέα <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: Εμφανίστε μόνο τον αριθμό σελίδας για UI
  • PageTokenHistory: λεξικό χαρτογράφηση των αριθμών σελίδων σε σημεία για την πίσω πλοήγηση
  • 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>

Ο βοηθός ετικέτας αυτόματα:

  • Κατανέμει το ιστορικό του σημειώματος σε παραμέτρους ερωτήσεων
  • Κατασκευάζει URL πλοήγησης με τις κατάλληλες μάρκες
  • Απενεργοποιεί το "Προηγούμενο" όταν βρίσκεται στη σελίδα 1
  • Απενεργοποιεί το "Επόμενο" όταν HasMoreResults είναι ψεύτικη
  • Διατηρεί όλες τις άλλες παραμέτρους ερωτημάτων (αναζήτηση, φίλτρα κ.λπ.)

Token History for Backward Navigation

Η ιδιοφυΐα της προσέγγισης της ιστορίας είναι ότι είναι εντελώς προαιρετικό. Αν χρειάζεστε μόνο "Επόμενη" πλοήγηση (ατελείωτη περγαμηνή, για παράδειγμα), μπορείτε να αγνοήσετε εντελώς την ιστορία των μαρκών:

<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 παράμετρος περιέχει το κωδικοποιημένο λεξικό, καθιστώντας την πίσω πλοήγηση απρόσκοπτη.

Numbered Page Navigation

Μια από τις πιο σημαντικές βελτιώσεις 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&currentPage=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 Απλοποιημένη Κινεζική () ()

Όλο το κείμενο είναι εντοπισμένο, συμπεριλαμβανομένων:

  • Προηγούμενες/Επόμενη/Πρώτη/Τελευταίες ετικέτες κουμπιών
  • Συνοπτικό κείμενο σελίδας ("Εμφάνιση X έως Y των αντικειμένων Z")
  • Ετικέτες ARIA για προσβασιμότητα
  • Ετικέτα μεγέθους σελίδας ("Items per page")

Το σύστημα εντοπισμού τροφοδοτείται από .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 μερικές ενημερώσεις προβολής διατηρούν την επιλεγμένη γλώσσα.


JavaScript Modes

Μία από τις πιο σημαντικές βελτιώσεις στο 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 απαιτείται. Ιδανικό για:

  • Απαιτήσεις προσβασιμότητας
  • Σενάρια προοδευτικής ενίσχυσης
  • Περιβάλλοντα όπου η JavaScript είναι απενεργοποιημένη
  • SEO-κριτικές σελίδες όπου θέλετε crawler-φιλική πλοήγηση

Η ομορφιά αυτού του συστήματος είναι ότι όλες οι λειτουργίες διατηρούν τις υπάρχουσες παραμέτρους ερωτημάτων σας. Είτε φιλτράρετε ανά κατηγορία, ψάχνετε, ή ταξινομήσετε, η σελιδοποίηση διατηρεί την κατάστασή σας αυτόματα.

Μετανάστευση από τη χρήση-htmx {# Μετανάστευση από τη χρήση-htmx}

Για την οπισθοδρομική συμβατότητα, το παλιό 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 Mode

Η NoJS ViewType συνδυάζει μηδέν JavaScript με το απλό στυλ CSS:

<paging
    model="Model"
    view-type="NoJS"
    show-pagesize="true" />

Βασικές διαφορές από άλλους τύπους προβολής:

  1. Η πλοήγηση χρησιμοποιεί συνδέσμους άγκυρας, όχι κουμπιά:
<a href="/Products?page=2" class="pager-button">Next ›</a>
  1. Ο επιλογέας μεγέθους σελίδας είναι μια μορφή:
<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> Το κουμπί εξασφαλίζει πλήρη λειτουργικότητα όταν δεν είναι.


Διατήρηση παραμέτρου URL

Μια από τις πιο απογοητευτικές πτυχές των εφαρμογών 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&currentPage=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: Δοκιμάστε σχολαστικά

Εκτελέστε την εφαρμογή και τη δοκιμή σας:

  • Πλοήγηση σελίδας
  • Αλλαγές μεγέθους σελίδας
  • HTMX μερικές ενημερώσεις (εάν χρησιμοποιείτε HTMX)
  • Φίλτρο/συντήρηση αναζήτησης
  • Κινητή απόκριση

Νέα χαρακτηριστικά για την υιοθέτηση

Μόλις μεταναστεύσει, σκεφτείτε την υιοθέτηση αυτών των νέων χαρακτηριστικών:

Τοπικοποίηση:

<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 Application

Η βιβλιοθήκη περιλαμβάνει μια ολοκληρωμένη demo εφαρμογή επιδεικνύοντας όλα τα χαρακτηριστικά γνωρίσματα. Μπορείτε να το τρέξετε τοπικά ή να το δείτε στο site demo (Έρχεται σύντομα).

Τρέχει το Demo τοπικά:

git clone https://github.com/scottgal/mostlylucid.pagingtaghelper.git
cd mostlylucid.pagingtaghelper/mostlylucid.pagingtaghelper.sample
dotnet run

Πλοηγηθείτε στο https://localhost:5001 για να εξερευνήσετε:

  1. Basic Pagination με Μοντέλο - Παραδοσιακό pagination με SQL-style pagination
  2. HTMX Integration - Δυναμικές ενημερώσεις σελίδας χωρίς πλήρη reloads σελίδα
  3. Αναζήτηση με HTMX - Συνδυασμένη αναζήτηση και επικόλληση
  4. Απλή CSS - Δεν υπάρχουν εξαρτήσεις πλαίσιο
  5. Καθαρός άνεμος ουράς - Tailwindss χωρίς DaisyUI
  6. Δεν JavaScript - Πλήρως λειτουργική zero-JS pagination
  7. Τρόποι JavaScript - Και οι πέντε τρόποι JS έδειξαν πλάι-πλάι
  8. Ταξινόμηση σελίδας - Sortable κεφαλίδες με HTMX
  9. Σελίδα Ταξινόμηση αριθ. HTMX - Sortable κεφαλίδες με πλήρες φορτίο σελίδας
  10. Μέγεθος σελίδας με HTMX - Δυναμικές αλλαγές μεγέθους σελίδας
  11. Μέγεθος σελίδας αριθ. HTMX - Μέγεθος σελίδας με υποβολή εντύπου
  12. Συνέχισε τον βομβητή - NoSQL-style συμβολικό-based pagination
  13. Τοπικοποίηση - Γλωσσικός επιλογέας με 8 γλώσσες

Κάθε demo περιλαμβάνει:

  • Πηγαίος κώδικας εργασίας
  • Επεξήγηση της τεχνικής
  • Σύνδεση με την εφαρμογή GitHub
  • Διαδραστικά χειριστήρια για πειραματισμό

Συμπέρασμα

Έκδοση 1.0.0 αντιπροσωπεύει ένα σημαντικό ορόσημο για τη βιβλιοθήκη PagingTagHelper. Τι ξεκίνησε ως μια απλή απαίτηση εργασίας έχει εξελιχθεί σε μια ολοκληρωμένη, έτοιμη για την παραγωγή λύση σελιδοποίησης που χειρίζεται:

  • Παραδοσιακή σελιδοποίηση SQL με όφσετ/όριο
  • NoSQL continue pagination για Cosmos DB, DynamoDB, κλπ.
  • Πολυγλωσσική τοπικοποίηση για το παγκόσμιο κοινό
  • Ευέλικτες λειτουργίες JavaScript από HTMX σε μηδέν-JavaScript
  • Πολλαπλά πλαίσια CSS από το DaisyUI έως το καθαρό Tailwind σε κανένα
  • Έξυπνη διατήρηση παραμέτρων σε όλη την πλοήγηση
  • Υποστήριξη πλήρους προσβασιμότητας με ετικέτες ARIA και πλοήγηση με πληκτρολόγιο

Η βιβλιοθήκη έχει δοκιμαστεί με λήψεις 1.7k+ και είναι έτοιμη για παραγωγή. Και οι 106 δοκιμές μονάδων περνούν, και η ολοκληρωμένη εφαρμογή demo παρουσιάζει πρότυπα χρήσης πραγματικού κόσμου.

Ποιο είναι το επόμενο;

Μελλοντικές βελτιώσεις Σκέφτομαι:

  • Πρόσθετη υποστήριξη-πλαίσιο CSS (Material UI, Bulma)
  • Περισσότερες γλώσσες εντοπισμού (κοινοτικές συνεισφορές ευπρόσδεκτες!)
  • Εξαρτήματα Blazor πλαϊνών διακομιστή
  • Ενισχυμένη προσβασιμότητα για μεγάλα σύνολα δεδομένων

Ξεκινήστε

Εγκατάσταση μέσω NuGet:

dotnet add package mostlylucid.pagingtaghelper --version 1.0.0

Ελέγξτε τα έγγραφα:

Ερωτήσεις, σχόλια ή συνεισφορές; Ανοίξτε ένα θέμα για το GitHub ή επικοινωνήστε στο Twitter @scottgal.

Καλή περιπέτεια!

Finding related posts...
logo

© 2026 Scott Galloway — Unlicense — All content and source code on this site is free to use, copy, modify, and sell.