Back to "PaggingTagHelper v1.0: Enterprise-Ready Pagination for Modern ASP.NET Core"

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

Alpine.js ASP.NET Core HTMX Javascript Nuget PagingTagHelper TagHelper

PaggingTagHelper v1.0: Enterprise-Ready Pagination for Modern ASP.NET Core

Friday, 07 November 2025

HUOMAUTUS: TULOSSA MYÖHEMMIN, viimeistelen vain sen. Seuraa GitHubia! .

Tämä on vain näyttääkseni teille kaikille, että olen edistymässä tämän hallinnan kanssa!

Johdanto

Kuukausien evoluution ja yhteisöltä saadun arvokkaan palautteen (5.7k+-lataukset!) jälkeen olen innoissani voidessani ilmoittaa, että PaggingTagHelper-kirjasto on saavuttanut version 1.0.0. Tämä ei ole vain versionumerokuoppa – se edustaa kirjaston täydellistä kypsymistä, jossa on ominaisuuksia, jotka tekevät siitä sopivan todellisiin tuotantosovelluksiin.

Jos olet seurannut tätä sarjaa, muistat, että aloitimme paljaat luulliset hakulaitteet, lisätty kiltimpiä otsikoita, ja uutettu Sivukokosäätimet. Versio 1.0.0 vie kaiken oppimamme ja lisää kriittisiä yritteliäisyysominaisuuksia:

  • Jatka Token Pagination NoSQL-tietokannat (Cosmos DB, DynamoDB, Azure Table Storage)
  • Monikielinen lokalisointi 8 kielen tuki ulos laatikosta
  • Joustavat javascript-tilat HTMX:stä nollaksi JavaScriptiin
  • Puhtaat myötätuulinäkymät ilman DaisyUI:n riippuvuuksia
  • Älykäs URL-parametrin säilyttäminen Kaikki navigaatiot
  • HTMX 2.0,4 päivitys takautuvasti yhteensopivaksi

Sukellataan jokaiseen näistä piirteistä ja katsotaan, miten ne toimivat yhdessä luodakseen todella joustavan paginaatioratkaisun.

NuGet NuGet

Jatkoa Token Paginationille

Perinteinen paginointi toimii kauniisti SQL-tietokannoilla, joissa voit helposti SKIP sekä TAKE Mutta mitä tapahtuu, kun on mukana NoSQL-tietokantoja, kuten Cosmos DB, DynamoDB tai Azure Table Storage? Nämä tietokannat eivät tue offset-pohjaista paginointia – sen sijaan ne käyttävät jatkotolpat.

Token-based Pagination-ymmärrän-token-based-pagination-ymmärtäminen

Näin jatkokeittopaginointi eroaa perinteisestä hakutaidosta:

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

Perinteinen haku:

  • Määrität tarkalleen, mitkä tietueet on haettava (OFFSET/LIMIT)
  • Voit hypätä mille tahansa sivulle suoraan
  • Tietokannan täytyy käydä läpi kaikki aiemmat tietueet

Token-pohjainen haku:

  • Tietokanta palauttaa epäselvän poletin, joka edustaa "missä jatkaa"
  • Token-muoto on tietokantakohtainen ja asiakkaan kannalta vaikeaselkoinen
  • Eteenpäin navigointi on luonnollista, takautuva navigointi vaatii symbolista historiaa

Jatkosivun toteutus Jatkosivun toteutus}

Uusi <continuation-pager> Tag-auttaja tekee token-based paginationin toteuttamisesta mutkatonta. Luo ensin malli, joka toteuttaa 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();
}

Liitäntä on minimaalinen mutta tehokas. Katsotaanpa, mitä jokainen ominaisuus tekee:

  • NextPageToken: Seuraavan sivun hakutunnus (tietokannan tarjoama)
  • HasMoreResults: Boolean ilmoittaa, onko sivuja enemmän
  • PageSize: Tuotteita sivulla
  • CurrentPage: Vain näytön sivunumero UI:lle
  • PageTokenHistory: Dictionary kartoitussivujen numerot takautuvan navigoinnin kuponkeihin
  • ViewType: Mitä CSS-kehystä käytetään renderointiin

Nyt toteutetaan ohjaintoiminto, joka simuloi Cosmos DB -tyylistä paginaatiota:

[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);
}

Tämä toteutus osoittaa, kuinka symbolinen historia mahdollistaa takautuvan navigoinnin. Ilman sitä jatkokylttipaginointi tukisi vain "Next"-painikkeita. Pitämällä yllä sanakirjaa sivusta toiseen -kartoituksista voimme tukea sekä "Ennen" että "Seuraavan" navigointia.

Tässä on kuponkikertymän virtaama visualisoituna:

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-näkymässäsi jatkohakulaitteen käyttö on yksinkertaista:

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

Tagiavustin automaattisesti:

  • Serialisoi merkkihistorian kyselymuuttujiksi
  • Rakentaa navigoinnin URL-osoitteita oikeilla tunnuksilla
  • Poistaa "edeltävän" käytöstä sivulla 1
  • Poistaa "Seuraavan" käytöstä, kun HasMoreResults on väärä
  • Säilyttää kaikki muut kyselyparametrit (haku, suodattimet jne.)

Token History for Backward Navigation token-historiikki

Nimellishistorian nerokkuus on se, että se on täysin valinnainen. Jos tarvitset vain "Seuraavan" navigoinnin (esim. finiittikäärön), voit jättää symbolihistorian kokonaan huomiotta:

<continuation-pager
    model="Model"
    enable-token-accumulation="false"
    show-page-number="false" />

Tämä tekee vain "Seuraavan" painikkeen ilman sivuindikaattoreita tai historianhallintaa.

Täyden navigoinnin osalta symbolihistoria sarjataan automaattisesti JSON:ksi kyselyn merkkijonossa. URL näyttää tältä symbolihistoriallaan:

/Products?currentPage=3&pageSize=25&pageToken=abc123&tokenHistory=%7B%221%22%3A%22xyz789%22%2C%222%22%3A%22abc123%22%7D

Erytropoietiini tokenHistory Parametri sisältää koodatun sanakirjan, mikä tekee takautuvasta navigoinnista saumatonta.

Numbered Page Navigation Numbered Page Navigation}

Yksi tärkeimmistä UX-parannuksista jatkohakulaitteessa on Numeroidut sivupainikkeet. Kun navigoit eteenpäin, pager näyttää napsautettavia sivunumeroita kaikille vierailluille sivuille:

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)

Tämä tarjoaa perinteisen pagination UX:n säilyttäen samalla tokend-pohjaisen backend-arkkitehtuurin. Toteutus tallentaa kuponkeja jokaiselle vieraillulle sivulle, mikä mahdollistaa suoran navigoinnin kaikille aiemmin käytetyille sivuille.

Historian kasvun rajoittaminen:

Rajattoman muistin käytön estämiseksi aseta max-history-pages (oletus: 20):

<continuation-pager
    model="Model"
    max-history-pages="50"
    show-page-number="true" />

Kun raja saavutetaan, vanhimmat sivumerkit leikataan automaattisesti.

Kriittinen: Query Parametrin säilyttäminen

Tämä on hakulaitteen jatkototeutuksen tärkein ominaisuus.

Jatkomerkit ovat voimassa vain samassa kyselykontekstissa (suodattimet, lajit, haut), joka loi ne. Tokentin käyttö eri kyselyparametrien kanssa palauttaa virheelliset tiedot tai epäonnistuu kokonaan.

Jatkohakulaite säilyttää ALL-kyselyparametrit automaattisesti omia lukuun ottamatta:

<!-- 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. -->

Voit tarvittaessa poistaa tämän käytöksen käytöstä:

<continuation-pager
    model="Model"
    preserve-query-parameters="false" />

Mutta tämä on lannistui vahvasti Ellet ole täysin varma, että viestisi eivät ole riippuvaisia kyselykontekstista.

Miksi tällä on merkitystä:

Cosmos DB -esimerkki:

// 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!
);

Jatkohakulaitteen automaattinen parametrien säilyttäminen takaa, että poletteja käytetään aina alkuperäisessä kyselykontekstissaan.


Lokalisointituki _ lokalisointi-tuki}

Modernit sovellukset palvelevat maailmanlaajuista yleisöä, ja paginointiohjainten on puhuttava käyttäjien kieltä. Versio 1.0.0 sisältää laajan lokalisointituen, joka on rakennettu suoraan kirjastoon.

Sisäänrakennetut kielet

Kirjaston laivat, joissa on kahdeksan kielen käännökset:

Koodi "Kieli" |------|----------| | en Englanti (oletus) | de Saksa (Deutsch) | es Espanja (Español) | fr Ranska (Français) | it Italia (Italia) | pt Portugali (Português) | ja Japanilainen (anteeksi) | zh-Hans Kiinalainen yksinkertaistettu (anteeksi)

Kaikki teksti on lokalisoitu, mukaan lukien:

  • Edellinen/Seuraava/Ensimmäinen/Viimeinen painiketarrat
  • Sivun yhteenvetoteksti ("Showing X to Y of Z Items")
  • ARIA-merkit saavutettavuutta varten
  • Sivun kokomerkintä ("Esimerkit sivulla")

Lokalisointijärjestelmän voimanlähteenä on .resx Resource-tiedostot, jolloin on helppo lisätä omia kieliä. Kaikki resource-tiedostot ovat mostlylucid.pagingtaghelper/Resources/.

Paikallistamisen käyttö localization-usage}

Lokalisointi on yksinkertaista. Lisää vain language Ominaisuus:

<paging
    model="Model"
    language="de"
    show-summary="true"
    first-last-navigation="true" />

Tämä tarkoittaa, että kaikki teksti on saksaksi:

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

Dynaamista kielenvaihtoa varten, joka perustuu käyttäjän asetuksiin, aseta kieli ohjaimeen:

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);
}

Luo sitten näkemyksesi mukaan kielivalitsija:

@{
    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" />

Voit myös ohittaa yksittäiset tekstijonot, mutta voit silti hyödyntää lokalisointia muille elementeille:

<paging
    model="Model"
    language="ja"
    previous-page-text="戻る"
    next-page-text="次へ"
    summary-template="全{TotalItems}件中 {StartItem}~{EndItem}件を表示" />

Erytropoietiini PagingLocalizer Palvelu käsittelee kulttuurikohtaista muotoilua automaattisesti. Jos ohitat väärän kielikoodin, se palaa sulavasti englannille.

HTMX-integraatiota varten haluat säilyttää kielen kaikissa pyynnöissä:

<script>
    htmx.on('htmx:configRequest', function(event) {
        if (event.detail.path.includes('/Products')) {
            event.detail.parameters.language = '@selectedLanguage';
        }
    });
</script>

Näin varmistetaan, että HTMX:n osakatselupäivitykset ylläpitävät valittua kieltä.


Javascript-moodit

Yksi merkittävimmistä v1.0.0:n parannuksista on joustavien JavaScript-tilojen käyttöönotto. Aiemmin sinulla oli boolean valinta: use-htmx="true" tai use-htmx="false". Nyt sinulla on viisi eri tilaa, joista jokainen on optimoitu erilaisiin skenaarioihin.

Käytössä olevat tilat

Tässä on Javascript-tilojen täydellinen jakautuminen:

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]

Katsotaan jokainen toimintatila:

1. HTMX-tila (Default)

<paging
    model="Model"
    js-mode="HTMX"
    htmx-target="#results-container" />

Renderöijät:

<button hx-get="/Products?page=2" hx-target="#results-container" hx-swap="outerHTML">
    Next ›
</button>

Täydellinen dynaamisille sivupäivityksille ilman koko sivun uudelleenlatauksia. Tämä on suosituin tila nykyisille ASP.NET Core -sovelluksille.

2. HTMXAlpine Mode

<paging
    model="Model"
    js-mode="HTMXWithAlpine"
    htmx-target="#results-container" />

Renderöijät:

<button
    x-data
    hx-get="/Products?page=2"
    hx-target="#results-container"
    hx-swap="outerHTML">
    Next ›
</button>

Yhdistää HTMX:n navigaatioon Alppien.js:n kanssa ja lisää asiakaspuolen vuorovaikutusta. Käytä tätä, kun tarvitset reaktiivisia UI-elementtejä paginoinnin rinnalla (latausmittarit, animaatiot, asiakaspuolen validointi).

3. Alppitila

<paging
    model="Model"
    js-mode="Alpine" />

Renderöijät:

<button
    x-data
    @click="window.location.href = '/Products?page=2'">
    Next ›
</button>

Puhtaat alppi.js ilman HTMX:ää. Hyödyllistä, kun käytät alppi.j:itä, mutta et halua HTMX:n riippuvuuksia.

4. PlainJS-tila

<paging
    model="Model"
    js-mode="PlainJS" />

Renderöijät:

<button onclick="window.location.href = '/Products?page=2'">
    Next ›
</button>

Ei kehysriitoja, vain vanilja JavaScript. Tähän tilaan kuuluu myös sivunkokomuutosten auttaja:

@Html.PageSizeOnchangeSnippet()

Tämä ruiskuttaa tarvittavat JavaScript-ohjeet sivun koon laskuun ilman HTMX:ää.

5. NoJS-tila

<paging
    model="Model"
    js-mode="NoJS" />

Renderöijät:

<!-- 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 vaaditaan. Täydellinen:

  • Saavutettavuusvaatimukset
  • Progressiivista tehostamista koskevat skenaariot
  • Ympäristöt, joissa JavaScript on pois päältä
  • SEO-kriittiset sivut, joihin haluat ryömijäystävällisen navigoinnin

Tämän järjestelmän kauneus on se, että Kaikki tilat säilyttävät nykyiset kyselyparametrisi. Suodatatpa sitten kategorioittain, etsimällä tai lajittelemalla, paginaatio ylläpitää tilaasi automaattisesti.

Muutto käytöstä-htmx Maahanmuutto käytöstä-htmx

Taaksepäin yhteensopivuus, vanha use-htmx Ominaisuus toimii yhä:

<!-- 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" -->

Suosittelen kuitenkin muuttoa uuteen js-mode Selkeyden ominaisuus:

<!-- New syntax (recommended) -->
<paging model="Model" js-mode="HTMX" />
<paging model="Model" js-mode="PlainJS" />

ViewType Enhancements viewtype-enhancements}

Versiossa 1.0.0 esitellään kaksi tärkeää ViewType-lisäystä, joissa käsitellään yhteisiä reaalimaailman skenaarioita.

Puhdasta myötätuulta

Aiemmin, jos haluat TailwindCSS-tyylin, sinulla on TailwindAndDaisy Näkymä, jossa käytetään DaisyUI:n komponentteja. Tämä on hienoa, jos käytät jo DaisyUI:ta, mutta entä jos haluat puhtaan Tailwindin ilman DaisyUI:n riippuvuutta?

Syötä ViewType.Tailwind:

<paging
    model="Model"
    view-type="Tailwind" />

Tämä tarkoittaa, että käytetään vain normaaleja Tailwind-apuohjelmia:

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

Ei tarvitse. btn, badge, tai join Luokat – vain puhdasta myötätuulta. Näin voit hallita täydellisesti tyylittelyä ilman komponenttikirjaston riippuvuuksia.

Vertailu:

ViewType CSS Framework Komponenttikirjasto Use Case |----------|---------------|-------------------|----------| | TailwindAndDaisy Kääntötuulessa DaisyUI-projektit, joissa käytetään jo DaisyUI-hanketta | Tailwind Kääntötuulta ei ole olemassa Puhtaan myötätuulen projekteja | Bootstrap Bootstrap Bootstrap Komponentit Bootstrap-hankkeet | Plain Yhteyttä CSS:ään Ei mitään kehystä riippuvuussuhteita | NoJS Uppoutunut CSS None Zero JavaScript requirements

NoJS-tila nojs-mode

Erytropoietiini NoJS ViewType yhdistää nolla JavaScriptin tavalliseen CSS-tyyliin:

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

Tärkeimmät erot muihin katsojatyyppeihin:

  1. Navigointi käyttää ankkurilinkkejä, ei painikkeita:
<a href="/Products?page=2" class="pager-button">Next ›</a>
  1. Sivukokovalitsin on lomake:
<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>

Erytropoietiini onchange="this.form.submit()" tarjoaa mukavuutta, kun JavaScript on saatavilla, mutta <noscript> Nappi takaa täyden toimivuuden silloin, kun se ei ole.


URL-parametrin säilyttäminen

Yksi turhauttavimmista puolista paginaation toteutuksessa on suodattimien, hakutermien tai järjestyksen menettäminen sivujen välillä navigoidessa. Versio 1.0.0 ratkaisee tämän tyylikkäästi Säilytetään automaattisesti kaikki kyselyparametrit paitsi paginaatio-ohjauksen omat parametrit.

Tämä ominaisuus toimii samalla tavalla eri puolilla Sekä säännölliset hakulaitteet että jatkohakulaitteet, ja yli Kaikki Javascript-tilat ja ViewTypes.

Näin se toimii sisäisesti:

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)}"));
}

Tämä lähestymistapa tarkoittaa seuraavaa:

Skenaario 1: Etsi + Paginaatio

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

Skenaario 2: Lajittelu + Paginaatio

Initial URL: /Products?orderBy=price&descending=true&page=1
Click Page 3: /Products?orderBy=price&descending=true&page=3

Skenaario 3: Jatka suodatinten kanssa

Initial URL: /Products?category=electronics&brand=acme
Click Next: /Products?category=electronics&brand=acme&currentPage=2&pageToken=abc123&tokenHistory={...}

Sama säilytys toimii muodossa (NoJS-tilassa). Kun teet sivun kokoista muotoa, näkymä sisältää automaattisesti piilotetut syötteet kaikille ei-paginointiparametreille:

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

Tämä toimii saumattomasti Kaikki Javascript-tilat ja kaikki ViewTypes. Sinun ei koskaan tarvitse hallita kyselyjonon lisäystä käsin.


Maahanmuutto-opas

Esi-1.0-versioiden päivittäminen on yksinkertaista, mutta tiedossa on muutamia murto-osia.

Muutosten murtaminen

1. use-htmx on poissuljettu (mutta toimii kuitenkin)

Vanha:

<paging model="Model" use-htmx="true" />
<paging model="Model" use-htmx="false" />

Uusi (suositeltu):

<paging model="Model" js-mode="HTMX" />
<paging model="Model" js-mode="PlainJS" />

2. ViewType.TailwindAndDaisy käyttää nyt täysiä DaisyUI-komponentteja

Jos käytät ViewType.TailwindAndDaisy ja haluavat puhdasta myötätuulta ilman DaisyUI:ta:

Vanha käytös (puhdas myötätuuli):

<paging model="Model" view-type="TailwindAndDaisy" />

Uusi (vanhaa käytöstä varten):

<paging model="Model" view-type="Tailwind" />

Jatka käyttöä TailwindAndDaisy jos käytät DaisyUI-komponentteja:

<paging model="Model" view-type="TailwindAndDaisy" />
<!-- Uses btn, join, badge, select, etc. -->

3. HTMX päivitetty 2.0.4:ksi

Jos käytät HTMX:ää muualla sovelluksessasi, varmista yhteensopivuus HTMX 2.0.4.:n kanssa. Useimmat HTMX 1.x-koodit toimivat muuttumattomina, mutta tarkista HTMX 2.0 -siirtolaisopas Edge-tapausten osalta.

Askel kerrallaan tapahtuva muuttoliike

Vaihe 1: Päivitä NuGet-paketti

dotnet add package mostlylucid.pagingtaghelper --version 1.0.0

Vaihe 2: Tarkista olemassa olevaa koodiasi

Etsi koodipohjasta use-htmx Ominaisuudet:

# PowerShell
Get-ChildItem -Recurse -Include *.cshtml | Select-String "use-htmx"

# Bash/Git Bash
grep -r "use-htmx" --include="*.cshtml" .

Vaihe 3: Päivitys js-tilaan (suositeltu)

Korvaa use-htmx yy) kanssa, kun 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" />

Vaihe 4: Katsaus TailwindAndDaisyn käyttöön

Jos et ole asentanut DaisyUI:ta, mutta käytit TailwindAndDaisy:

- <paging model="Model" view-type="TailwindAndDaisy" />
+ <paging model="Model" view-type="Tailwind" />

Vaihe 5: Testaa perusteellisesti

Suorita hakemuksesi ja testaa:

  • Sivun navigointi
  • Sivun koon muutokset
  • HTMX:n osapäivitykset (jos käytetään HTMX:ää)
  • Suodatin/etsintäsäilytys
  • Liikkuva reagointikyky

Uusia ominaisuuksia otettava käyttöön

Kun olet muuttanut, harkitse näiden uusien piirteiden omaksumista:

Lokalisointi:

<paging
    model="Model"
    language="@CultureInfo.CurrentUICulture.TwoLetterISOLanguageName" />

Jatkosivu (jos käytetään NoSQL:ää):

<continuation-pager
    model="Model"
    htmx-target="#results-container"
    show-page-number="true" />

NoJS-tila (saatavuus):

<paging model="Model" js-mode="NoJS" />

Demosovellus demo-sovellus}

Kirjastossa on kattava demosovellus, jossa esitellään kaikki ominaisuudet. Voit tehdä sen paikallisesti tai katsoa sen demosivusto (tulee pian).

Demon pyörittäminen paikallisesti:

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

Navigoi https://localhost:5001 Tutkittavaksi:

  1. Peruspaginointi mallilla - Perinteinen haku SQL-tyylisellä paginaatiolla
  2. HTMX-integraatio - Dynaamiset sivupäivitykset ilman koko sivun uudelleenlatauksia
  3. Etsi HTMX:llä - Yhdistetty etsintä ja paginaatio
  4. Tavallinen CSS - Ei riippuvuuksia kehyksestä
  5. Puhdas myötätuuli - PerätuuliCSS ilman DaisyUI:ta
  6. Ei JavaScriptia - Täysin toimiva nolla-JS-paginaatio
  7. Javascript-tilat - Kaikki viisi JS-tilaa osoittivat vierekkäin
  8. Sivun lajittelu - Lajiteltavia otsikoita HTMX:llä
  9. Sivun lajittelu ei ole HTMX - Lajiteltavissa otsikoissa koko sivun kuormat
  10. Sivun koko HTMX:llä - Dynaaminen sivun koko muuttuu
  11. Sivun koko: HTMX - Sivun koko ja lomake
  12. Jatkosivu - NoSQL-tyylinen token-pohjainen paginaatio
  13. Paikallistaminen - Kielenvalitsija 8 kielellä

Jokaiseen demoon kuuluu:

  • Työskentelyn lähdekoodi
  • Tekniikan selitys
  • Linkki GitHubin täytäntöönpanoon
  • Interaktiiviset kontrollit kokeiluun

Päätelmät

Versio 1.0.0 on merkittävä virstanpylväs PaggingTagHelper-kirjastolle. Yksinkertaisena työvaatimuksena alkanut on kehittynyt kattavaksi, tuotantovalmiiksi paginointiratkaisuksi, joka käsittelee:

  • Perinteinen SQL-paginointi offset/limit
  • NoSQL-jatkujapaginointi Cosmos DB:lle, DynamoDB:lle jne.
  • Monikielinen lokalisointi globaalille yleisölle
  • Joustavat Javascript-tilat HTMX:stä nollaksi JavaScriptiin
  • Useita CSS-kehyksiä DaisyUI:sta pelkkään myötätuuleen ei yhtään
  • Älykäs parametrien säilyttäminen Kaikki navigaatiot
  • Täysi esteettömyystuki ARIA-tarroilla ja näppäimistön navigoinnilla

Kirjastoa on testattu 1,7k+-latauksilla, ja se on valmis tuotantokäyttöön. Kaikki 106 yksikköä läpäisevät kokeet, ja kattava demosovellus esittelee tosimaailman käyttömalleja.

Mitä seuraavaksi?

Tulevia parannuksia harkitsen:

  • CSS:n lisäkehystuki (Material UI, Bulma)
  • Lisää lokalisointikieliä (yhteisön panos on tervetullut!)
  • Palvelinpuolen Blazor-komponentit
  • Suurten tietoaineistojen parempi saavutettavuus

Aloita

Asenna NuGetin kautta:

dotnet add package mostlylucid.pagingtaghelper --version 1.0.0

Katso dokumentit:

Kysymyksiä, palautetta tai kannanottoja? Avaa kysymys GitHubissa tai ota yhteyttä Twitterissä @scottgal.

Happy paginating!

logo

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