PingerTagHelper v1. 0. 0: Advanced- Ready Pagination для Сучасного ядра ASP. NET (Українська (Ukrainian))

PingerTagHelper v1. 0. 0: Advanced- Ready Pagination для Сучасного ядра ASP. NET

Friday, 07 November 2025

//

20 minute read

ЩО МОЖНА ЗРОБИТИ. Слідуйте за GitHub! .

Це лише для того, щоб показати вам, що я роблю поступ за допомогою цього керування! Це буде варто чекати.

Вступ

Після місяців еволюції та цінних відгуків від спільноти (5. 7k+завантаження!), я з радістю оголошую, що бібліотека PergerTagHelper досягла версії 1. 0. 0. Це не просто номер версії, що представляє повне скорочення бібліотеки з особливостями, які роблять її придатною для реального світу, програм для виробництва.

Якщо ви дотримуєтеся цієї серії статей, ви пам'ятаєте, що ми почали з Плавлення голих кісток, додано придатні для впорядкування заголовки, і видобуто Регулятори розміру сторінки. Версія 1. 0. 0 приймає все, що ми вивчили, і додає критичні можливості підприємництва:

  • Безперервна пагінка для баз даних NoSQL (Coss DB, DynamoDB, Azure Content)
  • Локалізація багатьма мовами підтримує 8 мов
  • гнучкі режими JavaScript від HTMX до 0- JavaScript
  • Чисті погляди про зворотній бік без залежностей ДейзіУІ
  • Збереження параметрів адрес Smart у всіх навігаціях
  • HTMX 2. 0. 4 Оновлення з зворотною сумісністю

Давайте зануримося в кожну з цих особливостей і подивимось, як вони працюють разом, щоб створити справді гнучкий розчин дігіни.

NuGet NuGet

Безперервна пагінка Token

Традиційна пагінка чудово працює з базами даних SQL, де ви можете легко працювати. SKIP і TAKE Але що відбувається, коли ви працюєте з базами даних NoSQL на зразок Космос ДБ, DynamoDB або Azure Table Section? Позначки для продовження.

Розуміння Pagination з тоновим ключем {# розуміння заснованої на значенні- 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/LIITE)
  • Ви можете перейти на будь- яку сторінку напряму
  • База даних повинна сканувати всі попередні записи

Торг з тоном:

  • База даних повертає непрозорий знак, що позначає " де продовжувати "
  • Формат тону залежить від бази даних і непрозорий для клієнта
  • Переспрямувати навігацію - це натуральна, зворотна навігація вимагає записів елементів

Реалізація пейджера взаємозв' язки

Нові <continuation-pager> Помічник теґів робить реалізацію ідентифікатора простим. По- перше, створіть модель, яка реалізує роботу 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: булівське позначення, якщо є більше сторінок
  • PageSize: Елементи на сторінку
  • CurrentPage: Номер сторінки лише для інтерфейсу програми
  • PageTokenHistory: Номери сторінок словника з позначками для навігації назад
  • ViewType: Які оболонки CSS слід використовувати для відображення

Тепер давайте реалізуємо дію контролера, яка симулює пагінство у стилі DB:

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

Ця реалізація показує спосіб, у який записами журналу можна здійснювати навігацію назад. Без нього продовження ключа підтримуватиме лише кнопки " Далі ." Підтримуючи словник з картами, що відповідають сторінкам, ми можемо підтримувати як " Покрокове " так і " Далі ."

Ось потоковий потік нагромадження, представлений візуально:

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>$@product.Price.ToString("N2")</td>
                </tr>
            }
        </tbody>
    </table>

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

Помічник теґів автоматично:

  • Серіалізація журналу елементів у параметри запиту
  • Збирає адреси URL навігації з відповідними позначками
  • Вимикає " предивний " у випадку на сторінці 1
  • Вимикає " Далі ," якщо HasMoreResults є помилковим
  • Зберігає всі інші параметри запиту (пошук, фільтри тощо)

Журнал записів для навігації назад {# крапка- history}

Генієм підходу до історії заміток є те, що це зовсім не є обов' язковим. Якщо вам потрібна лише навігація " Далі " (наприклад, нескінченна гортання), ви можете повністю ігнорувати журнал елементів:

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

За допомогою цього пункту можна просто натиснути кнопку " Далі " без індикаторів сторінок або керування журналом.

Для повної навігації запис журналу елементів буде автоматично перелічено як JSON у рядку запиту. Ось як виглядатиме адреса URL з історією позначення:

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

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

За допомогою цього пункту можна встановити традиційну програму sugination UX з підтримкою архітектури сервера, заснованої на шаблонах. Позначки для кожного з відвіданих сторінок, за допомогою яких можна здійснювати безпосереднє пересування до будь- якої попередньо доступної сторінки.

Обмеження росту історії:

Щоб запобігти необмеженому використанню пам' яті, встановіть 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" />

Але це Наполегливо знеохочувався якщо ви абсолютно впевнені, що ваші жести не залежать від контексту запиту.

Чому це важливо:

Приклад Космічних об' єктів 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!
);

Автоматичне збереження параметрів Pager завжди буде використано з початковим контекстом запиту.


Підтримка локалізації {# localization-підтримка}

Сучасні програми обслуговують глобальну аудиторію, а засоби керування пагінцями мають говорити мовою ваших користувачів. Версія 1. 0. 0 включає у себе всебічну підтримку локалізації, вбудовану безпосередньо до бібліотеки.

Вбудовані мови {# вбудовані- у- мови}

Кораблі бібліотеки з перекладами на 8 мов:

♪ |------|----------| | en ♪ English (типовий) ♪ | de ♪ Німецька (Deutsch) ♪ | es Іспанська (Еспаньол) | fr ♪ Французька (Франсаїс) ♪ | it італійською (Італійно) | pt ♪ Португальська (Portugués) ♪ | ja ♪ Japan [ сягає] ♪ | zh-Hans ♪ Китайська спрощена [' сяє]

Весь текст локалізовано, зокрема:

  • Надписи кнопок Попередня/ Наступна/ Перша/ Остання
  • Текст резюме сторінки (" Показ X на Y елементів Z ")
  • Мітки для доступності в АРІЯ
  • Мітка розміру сторінки (" Items на сторінку ")

Система локалізації підтримується .resx файли ресурсів, що спрощує додавання ваших власних мов. Всі файли ресурсів є у mostlylucid.pagingtaghelper/Resources/.

Використання локалізації

Використання локалізації просте. Просто додайте language attribute:

<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}件を表示" />

The PagingLocalizer Служба автоматично працює з форматуванням, що залежить від культури. Якщо ви передаєте некоректний код мови, вона граціозно повертається до англійської.

Для інтеграції з HTMX вам слід зберегти мову через запити:

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

За допомогою цього пункту можна встановити, що оновлення часткового перегляду HTMX підтримують вибрану мову.


Режими JavaScript

Одним з найзначніших покращень у v1. 0. 0 є впровадження гнучких режимів JavaScript. Раніше у вас був булівський вибір: 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.

2. HTMXWithAlpine Model

<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 для навігації з альпійською. js для додаткової взаємодії з клієнтами. Скористайтеся цим, якщо вам потрібні активні елементи інтерфейсу користувача, разом з пагінацією (навантаження індикаторів, анімації, перевірки клієнтської сторони).

3. Альпійський режим

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

Відтворення:

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

Чисті альпійські.js без HTMX. Корисні, якщо ви вже використовуєте альпійські.js, але не хочете залежностей HTMX.

4. Режим PolyJS

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

Відтворення:

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

Без залежностей оболонки, просто ванільний JavaScript. У цьому режимі також передбачено допоміжний засіб для зміни розмірів сторінки:

@Html.PageSizeOnchangeSnippet()

Це вводить потрібні для обробки зміни розміру сторінки без 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>

Потрібне нульове JavaScript. Досконало для:

  • Вимоги доступності
  • Прогресивні сценарії покращення
  • Середовища, у яких JavaScript вимкнено
  • Сторінки критичних SEO, де ви хочете, щоб навігація була дружньою

Краса цієї системи в тому, що всі режими зберігають ваші існуючі параметри запиту. Чи фільтруєте ви за категорією, пошуком чи сортуванням, папіни підтримують ваш стан у автоматичному режимі.

Міграція від use- 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" />

ViewType Extensions

Версія 1. 0. 0 містить два важливі додатки ViewType, які стосуються типових сценаріїв реального світу.

Pure Tailwind

Раніше, якщо ви хотіли погладити TailWindCSS, ви отримали TailwindAndDaisy Перегляд, який використовує компоненти DaisUI. Це чудово, якщо ви вже використовуєте DaisiUI, але що, якщо вам потрібен чистий Tailwindwin без залежності ДейзіUI?

Ввести ViewType.Tailwind:

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

За допомогою цього пункту можна наказати програмі використовувати лише стандартні допоміжні класи Tailwin:

<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 Класи ведь просто вітряний вітер. Таким чином ви можете повністю контролювати няню без залежностей до бібліотек компонентів.

Порівняння:

View} {\cH00} {\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ > |----------|---------------|-------------------|----------| | TailwindAndDaisy Дівчата вже використовують DYUIIA | Tailwind Д_Е-Е-У-У-У-У-у-у-у-у-у-у-у-у-у-у-у-у | Bootstrap Передня частина пішохідної сумки, що в перекладі означає: | Plain Немає місця без об'єму | NoJS

Режим NoJS

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

The onchange="this.form.submit()" може бути зручніше, якщо JavaScript доступний, але з <noscript> button забезпечує повнофункціональну функціональність, якщо це не так.


Параметри URL Preservation {# адреса- parameter- preservation}

Одним з найогидніших аспектів реалізації пагінців є втрата ваших фільтрів, термінів пошуку або впорядкування під час навігації між сторінками. Версія 1. 0. 0 вирішує це елегантно автоматично зберігати всі параметри запиту, окрім власних параметрів керування пагінцями.

Ця можливість працює однаково і звичайні пейджери, і пейджери для продовження, і впоперек всі режими JavaScript і типи перегляду.

Ось як це працює всередині:

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: Пошук + Пагінія

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: Впорядкування + Послідовність

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). Під час відтворення форми розміру сторінки у області перегляду буде показано приховані вхідні дані для всіх параметрів, не пов' язаних з Pagination:

<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 і всі типи перегляду. Вам ніколи не потрібно керувати поширюванням рядка запиту вручну.


Путівник міграції {# мішанина- guide}

Оновлення з до 1, 000 версій є простим, але є декілька змін, що розриваються, про які слід пам'ятати.

Breaking changes

1. use-htmx застарілий (але працює)

Старий:

<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 тепер використовує повноцінні компоненти DaisUI

Якщо ви використовували ViewType.TailwindAndDaisy і хочуть чистого Хвіста без ДейзіУЇ:

Стара поведінка (чистий хвіст):

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

Створити (для отримання застарілої поведінки):

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

Залишити використання TailwindAndDaisy якщо ви використовуєте компоненти DaisUI:

<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: Оновити пакунок Nuce

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- mode (рекомендовано)

Замінити 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. Перегляньте використання вітрового вітра

Якщо ви не встановили DaisUI, але використовували 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" />

Демонстрація Програма

У бібліотеці передбачено комплексне демонстраційне показу всіх можливостей програми. Ви можете запустити його локально або переглянути його на сторінці програми. Демонстраційний сайт (приходить найближчим часом).

Запуск демонстрацій локально:

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

Перейти до https://localhost:5001 для вивчення:

  1. Базове налаштування за допомогою моделі - Традиційні пігмеї в стилі SQL.
  2. Інтеграція HTMX - Динамічне оновлення сторінок без перезавантаження повної сторінки
  3. Шукати з HTMX - Об' єднаний пошук і пагінія
  4. Простий CSS - Без базових залежностей
  5. Чистий вітровий вітер - Вторгнення у воду без Дейзіава.
  6. Немає JavaScript - Повнофункціональне парування з нулями у GS
  7. Режими JavaScript - Всі п'ять режимів JS демонстровано поруч
  8. Впорядкування сторінки - Впорядковані заголовки за допомогою HTMX
  9. Впорядкування сторінки Немає HTMX - Впорядковані заголовки з повним завантаженням сторінки
  10. Розмір сторінки з HTMX - Динамічні зміни розміру сторінки
  11. Розмір сторінки немає HTMX - Розмір сторінки з підпорядкуванням форми
  12. Пейджер безперервності - Пасиви у стилі без SQL
  13. Локалізація Засіб вибору мови 8 мовами

Кожна демонстрація включає:

  • Код роботи
  • Пояснення техніки
  • Посилання на реалізацію GitHub
  • Інтерактивні засоби для експерименту

Висновки

Версія 1. 0. 0 є важливим віхом для бібліотеки PingingTagHelper. Те, що почалося з простої робочої вимоги, перетворилось у комплексне, готове до виробництва рішення, яким керує:

  • Традиційне базікання SQL з відступом/ межею
  • Немає пагінців з продовженням ключа без SQL для Космічних ДБ, DynamoDB і т.д.
  • Локалізація багатьма мовами для глобальних аудиторій
  • гнучкі режими JavaScript від HTMX до 0- JavaScript
  • Декілька оболонок CSS Від Дейзіуї до чистого " Хвилища " до жодного
  • Кмітливе збереження параметрів у всіх навігаціях
  • Повна підтримка доступності з мітками АРІЇ та навігацією клавіатури

Бібліотеку було перевірено за допомогою звантажень 1. 7k+ і вона готова до використання. Пропущено всі 106 тестів одиниць, а комплексна демонстраційна програма показує шаблони використання у реальному світі.

Що далі?

Я розмірковую над майбутніми покращеннями:

  • Додаткова підтримка оболонок CSS (Material UI, Bulma)
  • З більшою кількістю місцевих мов (приймайте пожертви на комісію!)
  • Компоненти Blazor з боку сервера
  • Розширена доступність для великих наборів даних

Розпочати

Встановити за допомогою NuGet:

dotnet add package mostlylucid.pagingtaghelper --version 1.0.0

Зазирніть до документації:

Відкрийте журнал про GitHub або ознайомтеся з Твіттером @ 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.