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

<datetime class="hidden">2025-11-07T19:12</datetime>

<!--category-- Nuget, ASP.NET Core, HTMX, Alpine.js, Javascript, TagHelper, PagingTagHelper -->
> ЩО МОЖНА ЗРОБИТИ. [Слідуйте за GitHub! ](https://github.com/scottgal/mostlylucid.pagingtaghelper).

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

## Вступ

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

Якщо ви дотримуєтеся цієї серії статей, ви пам'ятаєте, що ми почали з [Плавлення голих кісток](https://www.mostlylucid.net/blog/pagingtaghelper), додано [придатні для впорядкування заголовки](https://www.mostlylucid.net/blog/pagingtaghelperpt11), і видобуто [Регулятори розміру сторінки](https://www.mostlylucid.net/blog/pagingtaghelperpt2). Версія 1. 0. 0 приймає все, що ми вивчили, і додає критичні можливості підприємництва:

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

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

[![NuGet](https://img.shields.io/nuget/v/mostlylucid.pagingtaghelper.svg)](https://www.nuget.org/packages/mostlylucid.pagingtaghelper)
[![NuGet](https://img.shields.io/nuget/dt/mostlylucid.pagingtaghelper.svg)](https://www.nuget.org/packages/mostlylucid.pagingtaghelper)

[TOC]

## Безперервна пагінка Token {# continent- tnoken- pagination}

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

### Розуміння Pagination з тоновим ключем {# розуміння заснованої на значенні- pagination}

Ось як продовження роду відрізняється від традиційної парування:

```mermaid
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)
- Ви можете перейти на будь- яку сторінку напряму
- База даних повинна сканувати всі попередні записи

**Торг з тоном:**

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

### Реалізація пейджера взаємозв' язки {# contentation- pager- implementation}

Нові `<continuation-pager>` Помічник теґів робить реалізацію ідентифікатора простим. По- перше, створіть модель, яка реалізує роботу `IContinuationPagingModel`:

```csharp
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:

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

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

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

```mermaid
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 використання пейджера продовження є простим:

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

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

```razor
<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):

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

Після досягнення обмеження найстаріші позначки сторінки буде автоматично обрізано.

### Критична: Оптимізація параметра запиту {# query- parameter- preservation}

**Це найважливіша функція реалізації продовжувача.**

Ключі продовження можна виконувати лише з однаковим контекстом запиту (фільтрами, різновидами, пошуками), які їх було створено. Використання ключа з різними параметрами запиту поверне неправильні дані або повністю поверне некоректні дані.

Пейджер продовження автоматично зберігає ВСІ параметри запитів, окрім власних:

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

За потреби, ви можете вимкнути таку поведінку:

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

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

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

Приклад Космічних об' єктів DB:

```csharp
// 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/`.

### Використання локалізації {# localization-usage}

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

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

Це переводить весь текст німецькою:

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

Для перемикання динамічної мови на основі параметрів користувача встановіть мову вашого контролера:

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

Потім, на вашу думку, створіть інструмент вибору мови:

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

Крім того, ви можете перевизначити окремі текстові рядки і скористатися з локалізації інших елементів:

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

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

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

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

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

---


## Режими JavaScript {# javascript- modes}

Одним з найзначніших покращень у v1. 0. 0 є впровадження гнучких режимів JavaScript. Раніше у вас був булівський вибір: `use-htmx="true"` або `use-htmx="false"`. Тепер у вас є п'ять різних режимів, кожен оптимізований для різних сценаріїв.

### Доступні режими {# доступні- режими}

Ось повний крах режимів JavaScript:

```mermaid
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 (типовий)**

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

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

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

Досконале оновлення для динамічних сторінок без перезавантаження сторінок. Це рекомендований режим для сучасних програм ASP. NET.

**2. HTMXWithAlpine Model**

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

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

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

Об' єднує HTMX для навігації з альпійською. js для додаткової взаємодії з клієнтами. Скористайтеся цим, якщо вам потрібні активні елементи інтерфейсу користувача, разом з пагінацією (навантаження індикаторів, анімації, перевірки клієнтської сторони).

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

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

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

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

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

**4. Режим PolyJS**

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

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

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

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

```razor
@Html.PageSizeOnchangeSnippet()
```

Це вводить потрібні для обробки зміни розміру сторінки без HTMX.

**5 Режим NoJS**

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

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

```html
<!-- 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 {# miguration- from- use- htmx}

Для зворотної сумісності старі `use-htmx` атрибут все ще працює:

```razor
<!-- 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` атрибут для прозорості:

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

---


## ViewType Extensions {# viewtype- enthances}

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

### Pure Tailwind {# fair- tailwind}

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

Ввести `ViewType.Tailwind`:

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

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

```html
<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 {# nojs- mode}

The `NoJS` ViewType об' єднує нуль JavaScript з простим стилі CSS:

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

Різниця ключів від інших типів перегляду:

1. **Навігація використовує посилання якоря**, не кнопки:

```html
<a href="/Products?page=2" class="pager-button">Next ›</a>
```

2. **Засіб вибору розміру сторінки є формою**:

```html
<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 і типи перегляду**.

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

```csharp
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:

```razor
<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` застарілий (але працює)**

Старий:

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

Створити (рекомендовано):

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

**2. ViewType. TailwindAndDaisy тепер використовує повноцінні компоненти DaisUI**

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

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

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

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

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

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

```razor
<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](https://htmx.org/migration-guide-htmx-1/) у справах краю.

### Переміщення за кроком

**Крок 1: Оновити пакунок Nuce**

```bash
dotnet add package mostlylucid.pagingtaghelper --version 1.0.0
```

**Крок 2: Перегляньте існуючий код**

Шукати у вашій базі коду за `use-htmx` атрибути:

```bash
# 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`:

```diff
- <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`:

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

**Крок 5: Ретельно перевіряйте**

Запустіть вашу програму і тест:

- Навігація сторінками
- Зміна розміру сторінки
- Частинні оновлення HTMX (якщо використовувати HTMX)
- Збереження фільтра/ пошуку
- Мобільна реакція

### Нові можливості для усиновлення

Після перельоту ви дізнаєтеся про такі нові можливості:

**Локалізація:**

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

**Пейджер продовження (якщо використовується NoSQL):**

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

**Режим NoJS (для доступності):**

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

---


## Демонстрація Програма {# demo- application}

У бібліотеці передбачено комплексне демонстраційне показу всіх можливостей програми. Ви можете запустити його локально або переглянути його на сторінці програми. [Демонстраційний сайт](https://paging-demo.mostlylucid.net) (приходить найближчим часом).

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

```bash
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:

```bash
dotnet add package mostlylucid.pagingtaghelper --version 1.0.0
```

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

- [Сховище GitHub](https://github.com/scottgal/mostlylucid.pagingtaghelper)
- [Пакунок NuGet](https://www.nuget.org/packages/mostlylucid.pagingtaghelper)
- [Повна документація](https://github.com/scottgal/mostlylucid.pagingtaghelper/tree/main/docs)

Відкрийте журнал про GitHub або ознайомтеся з Твіттером [@ scottgal](https://twitter.com/scottgal).

Счастливого вечера!