Back to "Paging TagHelfer v1. 0.0: 现代 ASP. NET 核心企业- 重新拉动"

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

Paging TagHelfer v1. 0.0: 现代 ASP. NET 核心企业- 重新拉动

Friday, 07 November 2025

即将到来,只是把 完成的触摸到它。 跟着GitHub走! .

这只是为了让你看到我在这个控制系统上的进展!

一. 导言 导言 导言 导言 导言 导言 一,导言 导言 导言 导言 导言 导言

在经过数月的进化和来自社区的宝贵反馈(5.7k+下载! )之后,我很兴奋地宣布PagageTagHelfer图书馆已经达到了1.0.0版本。 这不仅仅是一个版本数字突撞 — — 它代表图书馆完全成熟,具有适合现实世界、生产应用程序的特点。

如果你一直跟着这个系列, 你会记得我们从 光骨头呼叫加,加,加,加,加,加 可排序排序标题,并抽取 页面大小控件1.0.0版本采纳了我们学到的一切知识,并增加了关键的企业特征:

  • 继续调制 NoSQL 数据库(宇宙DB、DyNAMDB、Azure表格存储)
  • 多语种多语种本地化 框中支持 8 种语言
  • 弹性 JavaScript 模式 从 HTMX 到零 JavaScript
  • 纯粹反向视图 没有DisaudisUI依赖性
  • 智能 URL 参数保护 横横横横横横横的
  • HTMX2.0.4 HTMX2.0.4 具有后向兼容性升级

让我们潜入其中的每一个特征 看看他们如何合作 创造出一个真正灵活的方位解决方案

N Nuget 元数 N Nuget 元数

继续调控调控 * * * 继续调制调控 *

与 SQL 数据库一起,传统页形工作非常出色,您可以很容易地在 SQL 数据库中工作 SKIPTAKE 但是,如果你与诺斯QL数据库合作,比如宇宙DB、DynamoDB或Azure表格存储等数据库合作,会发生什么情况?这些数据库不支持基于抵消的页码 — — 相反,它们使用 续序号.

理解以当量基平面

以下是持续象征性的外观与传统传呼的不同之处:

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

传统传呼:

  • 您精确指定要检索的记录( OFFFSET/ LIMIT)
  • 您可以直接跳转到任何页面
  • 数据库必须扫描前所有记录

基于 Tokon 的调制调频呼叫 :

  • 数据库返回一个代表“ 在哪里继续” 的不透明标记
  • 调脚格式是数据库专用格式,对客户来说不透明
  • 前向导航是自然的,后向导航需要象征性历史

继续页面执行 @ 继续页面执行}

新的 <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: UI 的仅显示页码
  • PageTokenHistory: 用于后向导航的符号的词典映射页面编号
  • ViewType: CSS 框架用于制作

现在让我们执行一个模拟 Cosmos 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>[email protected]("N2")</td>
                </tr>
            }
        </tbody>
    </table>

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

标签助手自动 :

  • 将象征性历史序列化为查询参数
  • 以合适的符号构建导航 URL
  • 第1页时禁用“前一”
  • 禁用“ 下一步” 当 HasMoreResults 是假的
  • 保存所有其他查询参数( 搜索、 过滤器等)

后向导航的名曲历史

象征性历史方法的天才在于它是完全可选的。 如果您只需要“ 下一步” 导航( 例如无限滚动) , 您可以完全忽略象征性历史 :

<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

缩略 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)

这提供了传统的页码 UX, 并同时保持了基于符号的后端结构。 执行系统为每个访问的页面备有标牌, 允许直接浏览之前访问过的页面 。

限制历史增长:

为防止无约束内存使用,设置 max-history-pages (违约:20):

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

当限制达到时,最古老的页面符号会自动缩写 。

关键值: 查询参数保存 参数保存}

这是继续传呼器实施的最重要特点。

继续代号只有与生成该代号的相同的查询上下文( 过滤器、 种类、 搜索) 有效。 使用带有不同查询参数的代号将返回错误的数据或完全失败 。

续用 pelcer 自动保存所有查询参数, 除了它自己的参数以外 :

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

连续传呼机的自动参数保存确保标记始终与原始查询上下文一起使用。


本地化支持 本地化支持}

现代应用软件为全球受众服务,外形控制需要讲您的用户语言。 版本 1. 0.0 包括设置在图书馆中的全面本地化支持。

内建语言 @ 内建语言}

译为8种语言的图书馆船只:

代号: 语言: 语言: 语言: |------|----------| | en 英文( 默认) | de 德语( 德文) | es 西班牙语(埃斯帕尼奥尔语) | fr 法语( 法国) | it 意大利语( 意大利语) | pt 葡萄牙语( 葡萄牙文) | ja 日语 () | zh-Hans 中文简化 ()

所有文本均本地化,包括:

  • 上一/ 下/ 下/ 第一/ 最后一个按钮标签
  • 摘要文字 (“Z项的 X 至 Y 显示”)
  • 无障碍的ARIA标签
  • 页面大小标签 (“ 每页项目”)

本地化系统由 .resx 资源文件, 方便添加您自己的语言。 所有资源文件都在 mostlylucid.pagingtaghelper/Resources/.

本地化用量 本地化用量}

使用本地化是直截了当的。 language 属性 :

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

这样所有案文都用德文写成:

<!-- Previous button -->
<button>‹ Vorherige</button>

<!-- Summary -->
<div class="text-sm text-gray-600">
    Zeige 1 bis 10 von 256 Einträgen
</div>

<!-- Next button -->
<button>Nächste ›</button>

对于基于用户首选项的动态语言切换,请在您的控制器中设定语言 :

public async Task<IActionResult> Products(
    int page = 1,
    int pageSize = 10,
    string language = "en")
{
    var pagingModel = await GenerateModel(page, pageSize);
    ViewBag.SelectedLanguage = language;
    return View(pagingModel);
}

然后在您看来, 创建一个语言选择器 :

@{
    var selectedLanguage = ViewBag.SelectedLanguage as string ?? "en";
    var languages = new Dictionary<string, string>
    {
        { "en", "English" },
        { "de", "German" },
        { "es", "Spanish" },
        { "fr", "French" },
        { "it", "Italian" },
        { "pt", "Portuguese" },
        { "ja", "Japanese" },
        { "zh-Hans", "Chinese" }
    };
}

<select onchange="window.location.href='/Products?language=' + this.value">
    @foreach (var lang in languages)
    {
        <option value="@lang.Key" selected="@(lang.Key == selectedLanguage)">
            @lang.Value
        </option>
    }
</select>

<paging
    model="Model"
    language="@selectedLanguage"
    link-url="/Products" />

您还可以在仍然受益于其它元素本地化的同时, 推翻单个文本字符串 :

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

缩略 PagingLocalizer 服务会自动处理特定文化的格式。如果您通过了无效的语言代码,它会优雅地返回到英语中。

对于 HTMX 整合, 您会想要保存语言, 而不是请求 :

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

这样可以确保HTMX部分视图更新保留所选语文。


JavaScript 模式 @ javacramp- 模式}

在 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. HTMX随高山模式

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

与 Alpine.js 组合 HTMX, 用于导航, 用于额外的客户端互动。 当您需要反应性 UI 元素时, 请使用它与页码( 装入指示器、 动画、 客户端验证) 一同使用 。

3. 高山模式

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

重命名 :

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

纯Alpine.js没有HTMX。 当你已经使用Alpine.js但不想使用HTMX依赖性时, 有用。

4. 普通JS 模式

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

重命名 :

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

无框架依赖性, 只是 vanilla JavaScript。 此模式还包括用于更改页面大小的助手 :

@Html.PageSizeOnchangeSnippet()

这将输入所需的 JavaScript , 用于在不使用 HTMX 的情况下处理页面大小下拉修改 。

5. 无 JS 模式

<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 - 关键页面,其中您想要 爬行者方便的导航

这个体系的美丽之处在于 所有模式都保存您现有的查询参数。无论您是按类别、搜索或排序过滤,页码都自动保持状态。

来自使用- htmx 移民- 使用- 移民- 使用- htmx}

对于后向兼容性,旧的 use-htmx 属性仍然工作 :

<!-- Old syntax (still works) -->
<paging model="Model" use-htmx="true" />
<!-- Equivalent to js-mode="HTMX" -->

<paging model="Model" use-htmx="false" />
<!-- Equivalent to js-mode="PlainJS" -->

不过,我建议移到新的 js-mode 清晰度属性 :

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

视图类型增强@ view 类型增强}

1.0.0版本引入了两个重要的ViewType添加内容,涉及共同的现实世界情景。

纯尾风 纯尾风 纯尾风 纯尾风 纯尾风 * 纯尾风 * * 纯尾风 * * 纯尾风 * * 纯尾风 * * 纯尾风 * * 纯尾风 * * 纯尾风 * * * 纯尾风 * * * 纯尾风 * * * 纯尾风 * * * * 纯尾风 * * * * 纯尾风 * * * * 纯尾风 * * * * * * 纯尾风 * * * * * * 纯尾风 * * *

前情前情提要 如果你想要尾风CS的外型 TailwindAndDaisy 视图使用 DaisiUI 组件。 如果您已经使用 DaisyUI 组件, 这很好, 但如果您想要纯尾风, 没有 DaisyUI 依赖关系 ?

输入 ViewType.Tailwind:

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

这样就只能使用标准的尾风通用类:

<div class="flex gap-2 items-center">
    <button class="px-4 py-2 text-sm font-medium rounded-md bg-blue-600 text-white hover:bg-blue-700">
        ‹ Previous
    </button>

    <div class="px-3 py-1 text-sm font-medium bg-gray-100 dark:bg-gray-700 dark:text-white rounded-md">
        Page 1
    </div>

    <button class="px-4 py-2 text-sm font-medium rounded-md bg-blue-600 text-white hover:bg-blue-700">
        Next ›
    </button>
</div>

否 无 btn, badge,或 join 类 - 只是纯粹的尾风。 这使您能够完全控制没有组件库依赖的外形。

比较:

|----------|---------------|-------------------|----------| | TailwindAndDaisy 使用 DaisyUI 的項目 : DaisywindCSS : DaisiUI : DaisyUI : DaisyUI : DaisywindCSS : Daisui : DaisyUI : DaisyUI : | Tailwind | Bootstrap | Plain * 嵌入的CSS * 无 * 没有框架依赖 * | NoJS 嵌入的CSS 零 零 JavaScript 要求

NoJS 模式 nojs- 模式}

缩略 NoJS ViewType 组合零 JavaScript 和 普通 CSS 格式 :

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

与其他视图类型的关键差异 :

  1. 导航使用锁定链接,而不是按钮:
<a href="/Products?page=2" class="pager-button">Next ›</a>
  1. 页面大小选择器是一个窗体:
<form method="get" action="/Products" class="page-size-form">
    <!-- Preserves all current query parameters as hidden inputs -->
    <input type="hidden" name="search" value="laptop" />
    <input type="hidden" name="category" value="electronics" />

    <!-- Reset to page 1 when changing page size -->
    <input type="hidden" name="page" value="1" />

    <label for="pageSize">Items per page:</label>
    <select name="pageSize" onchange="this.form.submit()">
        <option value="10">10</option>
        <option value="25" selected>25</option>
        <option value="50">50</option>
    </select>

    <!-- Button visible when JavaScript is disabled -->
    <noscript>
        <button type="submit" class="page-size-button">Update</button>
    </noscript>
</form>

缩略 onchange="this.form.submit()" 当 JavaScript 有时提供方便, 但 <noscript> 按钮确保不完全功能时的完整功能。


URL 参数保存 url 参数保存}

平面执行中最令人沮丧的一个方面是失去您的过滤器、搜索条件或页面间导航时排序顺序。 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 模式) 。 在创建页面大小窗体时, 视图自动包含所有非粘贴参数的隐藏输入 :

<form method="get" action="@linkUrl" class="page-size-form">
    @* Preserve all existing query parameters except pageSize and page-related ones *@
    @foreach (var param in ViewContext.HttpContext.Request.Query)
    {
        if (!new[] { "pageSize", "currentPage", "pageToken", "tokenHistory" }
            .Contains(param.Key, StringComparer.OrdinalIgnoreCase))
        {
            <input type="hidden" name="@param.Key" value="@param.Value" />
        }
    }

    @* Reset to page 1 when changing page size *@
    <input type="hidden" name="currentPage" value="1" />

    <select name="pageSize" onchange="this.form.submit()">
        <!-- options -->
    </select>
</form>

这工作无缝无缝 全部 JavaScript 模式和所有视图类型。您不必手动管理查询字符串传播。


移民指南 移民指南 移民指南 移民指南 移民指南 移民指南 移民指南 移民指南 移民指南 移民指南 移民指南 移民指南 移民指南 移民指南 移民指南 移民指南 移民指南 移民指南 移民指南 移民指南 移民指南 移民指南 移民指南 移民指南 移民指南 移民指南 移民指南 移民指南 移民指南 移民指南 移民指南 移民指南 移民指南 移民指南 移民指南 移民指南 移民指南 移民指南 移民指南 移民指南 移民指南 移民指南 移民指南 移民指南 移民指南 移民指南 移民指南 移民指南 移民指南 移民指南 移民指南 移民指南 移民指南 移民指南 移民指南 移民指南

升级为1.0版前版本是直截了当的,但有一些突破性的变化需要了解。

断断更改

1. use-htmx 已经折旧(但仍在工作)

旧 :

<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.Taillwind和Daisy现在使用完整的DaisiUI组件

如果你使用 ViewType.TailwindAndDaisy 并想要纯粹的尾风 没有DisaiUI:

古老的行为( 纯粹的尾风) :

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

(以获得旧行为):

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

继续使用 TailwindAndDaisy 如果您使用 DisaiUI 组件 :

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

3. HTMX升级为2.0.4

如果您在应用程序的其他地方使用HTMX, 请确保与 HTMX 2. 4. 的兼容性。 大多数 HTMX 1. x 代码未变, 但审查 HTMX2.0 移徙指南 边缘情况。

逐步移徙

第1步:更新 NuGet 软件包

dotnet add package mostlylucid.pagingtaghelper --version 1.0.0

步骤2:审查现行代码

搜索您的代码库 use-htmx 属性 :

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

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

步骤3:更新js模式(建议)

替换 use-htmxjs-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:审查尾风和Daisy的使用情况

如果你没有安装DaisheUI,但正在使用 TailwindAndDaisy:

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

步骤5:彻底测试

运行您的应用程序和测试 :

  • 页面导航
  • 页面大小变化
  • HTMX部分更新(如果使用HTMX)
  • 过滤/搜索保护
  • 移动响应

要通过的新特点

一旦移民,考虑采用这些新的特征:

本地化:

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

继续 Pager( 如果使用 NoSQL ) :

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

NoJS 模式( 用于无障碍 ) :

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

Demo 应用程序 {dem - 应用程序}

图书馆包含一个全面的演示应用程序, 显示所有功能。 您可以在本地运行或查看 。 演示点站点 (即将到来)。

本地管理演示 :

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. 纯粹尾风 - 没有DaisyUI的尾风CSS
  6. 无 JavaScript 无 JavaScript - 完全正常零JS射程
  7. JavaScript 模式 - 所有五种联署材料模式并肩展示
  8. 页面排序 - 带有 HTMX 的可排序信头
  9. 页面排序无 HTMX - 具有全页负载的可排序页头
  10. 带有 HTMX 的页面大小 - 动态页面大小变化
  11. 页面大小无 HTMX - 页数与提交表格的页数
  12. 续续页面器 - NoSQL 风格的基于象征性的抛射
  13. 本地化 - 8种语言语言语言选择员

每个演示包括:

  • 工作源代码
  • 技术解释
  • 与 GitHub 执行链接
  • 用于实验的交互式控件

结论 结论 结论 结论 结论

  1. 版本 1. 0. 0 是PageTagHelper图书馆的一个重要里程碑。 以简单工作要求开始的,已经演变成一个全面、可生产、可处理的页码解决方案,处理:
  • SQL 传统SQL页数 抵消/限额
  • NOSQL 继续代号显示 Cosmos DB、DyMomoDB等的软件。
  • 多语言本地化 供全球受众使用
  • 弹性 JavaScript 模式 从 HTMX 到零 JavaScript
  • CSS 多个多 CSS 框架 从DisaiUI到纯尾风到无
  • 智能参数保护 横横横横横横横的
  • 完全无障碍支持 带有ARIA标签和键盘导航的ARIA标签和键盘导航

该图书馆经过战斗测试,下载了1.7k+的下载,可供制作使用,所有106个单元测试通过,综合演示应用程序展示了现实世界的使用模式。

下一个是什么?

我正在考虑:

  • CSS 额外框架支助(总UI、Bulma)
  • 更多本地化语言(欢迎社区捐款! )
  • Blazor 组件
  • 加强大型数据集的无障碍环境

开始

通过 NuGet 安装 :

dotnet add package mostlylucid.pagingtaghelper --version 1.0.0

检查文档 :

问题、反馈或贡献? @scottgal (苏格兰语).

欢乐快乐!

logo

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