ساخت pager tag helper

Tag helperها کلاس‌هایی هستند که می‌توانند برای HTML و تگ‌های خاص در ویوهای ASP.NET Core به کار روند. آن‌ها توسط کلاس‌های خودشان و پشتیبانی از فریم‌ورک تزریق وابستگی انعطاف‌پذیری بیشتری را ارائه می‌دهند. این مقاله نحوه ساخت pager tag helper برای پشتیبانی از نمایش نتایج صفحات در ویوهای ASP.NET Core را بررسی می‌کند.

ساخت pager tag helper

مثالی از tag helper

مثال tag helper می‌تواند از صفحه layout پیش‌فرض برنامه ASP.NET Core ساخته شود.

<environment include="Development">
    <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />
    <link rel="stylesheet" href="~/css/site.css" />
</environment>

تگ <environment> توسط کلاس خاصی پردازش می‌شود و بررسی می‌کند که هر گاه برنامه در حالت development در حال اجرا است، لینک‌ها را برای فایل‌های css تعریف شده بین تگ‌های <environment> بنویسد.

کلاس‌های Pager

قبل از اینکه برویم سراغ pager، بیایید نگاهی به مهم‌ترین کلاس‌هایی که در صفحه‌بندی استفاده می‌شوند بیندازیم. Pager از کلاس پایه زیر استفاده خواهد کرد زیرا تمام اطلاعات ضروری برای استخراج pager را حمل می‌کند.

public abstract class PagedResultBase
{
    public int CurrentPage { get; set; }
    public int PageCount { get; set; }
    public int PageSize { get; set; }
    public int RowCount { get; set; }
    public string LinkTemplate { get; set; }
 
    public int FirstRowOnPage
    {
         get { return (CurrentPage - 1) * PageSize + 1; }
    }
 
    public int LastRowOnPage
    {
        get { return Math.Min(CurrentPage * PageSize, RowCount); }
    }
}

همچنین به کلاس جنریک نیاز داریم تا نتایج صفحات را منتقل کنیم. به همین دلیل یک PagedResult<T> را تعریف می‌کنیم که PagedResultBase را توسعه داده و لیست را با نتایج در صفحه جاری اضافه می‌کند.

public class PagedResult<T> : PagedResultBase
{
    public IList<T> Results { get; set; }
 
    public PagedResult()
    {
        Results = new List<T>();
    }
}

اکنون ما همه کلاس‌های مورد نیاز برای ساخت pager را داریم.

Pager tag helper

در این پیاده‌سازی Pager tag helper ساده و کوتاه خواهد بود. تمام کلاس‌هایی که از PagedResultBase به عنوان مدل ارث برده‌اند را می‌پذیرد و با استفاده از مدل، خروجی HTML را برای pager ایجاد می‌کند. در viewها ما از آن همانند دستور زیر استفاده خواهیم کرد.

<pager pager-model="@Model"></pager>

در اینجا کد pager tag helper موجود است.

[HtmlTargetElement("pager", TagStructure = TagStructure.NormalOrSelfClosing)]
public class PagerTagHelper : TagHelper
{
    private readonly HttpContext _httpContext;
    private readonly IUrlHelper _urlHelper;
 
    [ViewContext]
    public ViewContext ViewContext { get; set; }
 
    public PagerTagHelper(IHttpContextAccessor accessor, IActionContextAccessor actionContextAccessor, IUrlHelperFactory urlHelperFactory)
    {
        _httpContext = accessor.HttpContext;
        _urlHelper = urlHelperFactory.GetUrlHelper(actionContextAccessor.ActionContext);
    }
 
    [HtmlAttributeName("pager-model")]
    public PagedResultBase Model { get; set; }
 
    public override void Process(TagHelperContext context, TagHelperOutput output)
    {            
        if(Model == null)
        {
            return;
        }
 
        if(Model.PageCount == 0)
        {
            return;
        }
 
        var action = ViewContext.RouteData.Values["action"].ToString();
        var urlTemplate = WebUtility.UrlDecode(_urlHelper.Action(action, new { page = "{0}" }));
        var request = _httpContext.Request;
        foreach (var key in request.Query.Keys)
        {
            if (key == "page")
            {
                continue;
            }
 
            urlTemplate += "&" + key + "=" + request.Query[key];
        }
 
        var startIndex = Math.Max(Model.CurrentPage - 5, 1);
        var finishIndex = Math.Min(Model.CurrentPage + 5, Model.PageCount);
 
        output.TagName = "";
        output.Content.AppendHtml("<ul class=\"pagination\">");
        AddPageLink(output, string.Format(urlTemplate, 1), "&laquo;");
 
        for (var i = startIndex; i <= finishIndex; i++)
        {
            if (i == Model.CurrentPage)
            {
                AddCurrentPageLink(output, i);
            }
            else
            {
                AddPageLink(output, string.Format(urlTemplate, i), i.ToString());
            }
        }
 
        AddPageLink(output, string.Format(urlTemplate, Model.PageCount), "&raquo;");
        output.Content.AppendHtml("</ul>");
    }
 
    private void AddPageLink(TagHelperOutput output, string url, string text)
    {            
        output.Content.AppendHtml("<li><a href=\"");
        output.Content.AppendHtml(url);
        output.Content.AppendHtml("\">");
        output.Content.AppendHtml(text);
        output.Content.AppendHtml("</a>");
        output.Content.AppendHtml("</li>");
    }
 
    private void AddCurrentPageLink(TagHelperOutput output, int page)
    {
        output.Content.AppendHtml("<li class=\"active\">");
        output.Content.AppendHtml("<span>");
        output.Content.AppendHtml(page.ToString());
        output.Content.AppendHtml("</span>");
        output.Content.AppendHtml("</li>");
    }
}

این تمام چیزی است که ما نیاز داریم تا نمایش pager را در viewها فراهم کنیم که از نتایج صفحات به عنوان مدل یا بخشی از مدل استفاده می‌کند.

ثبت pager tag helper

قبل از اینکه از pager tag helper استفاده کنیم، باید آن را در MVC ثبت کنیم. به طور خودکار نمایش داده نمی‌شود. برای این کار باید فایل ViewImports.cs_ را ویرایش کنیم.

@using DotNetPaging
@using DotNetPaging.AspNetCore
@using DotNetPaging.AspNetCore.Components
@using DotNetPaging.AspNetCore.Models
@using DotNetPaging.EFCore
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@addTagHelper *, DotNetPaging.AspNetCore

آخرین خط ضروری است تا همه tag helperهای ما را برای viewها ببرد.

نمایش نتایج صفحه‌بندی

حالا اجازه دهید یک اکشن کنترلر ساده بسازیم تا نتایج صفحه بندی را نمایش دهیم. من تمام کد را در اینجا نشان نمی‌دهم زیرا نحوه انجام کامل کار در ریپازیتوری GitHub من (gpeipman/DotNetPaging) در دسترس است.

public async Task<IActionResult> TagHelper(int page = 1)
{
    var releases = await _dataContext.PressReleases
                                     .OrderByDescending(p => p.ReleaseDate)
                                     .GetPagedAsync(page, 10);
    return View(releases);
}

در اینجا نمایش pager tag helper موجود است.

@model PagedResult<PressRelease>
@{
    ViewData["Title"] = "TagHelper";
}
 
<h2>Tag helper paging</h2>
 
<p>Results on this page are queried using synchronous method calls of Entity Framework Core.</p>
 
<table class="table">
    <thead>
        <tr>
            <th>Date</th>
            <th>Company</th>
            <th>Title</th>
        </tr>
    </thead>
    <tbody>
        @foreach (var release in Model.Results)
        {
            <tr>
                <td>@release.ReleaseDate.ToShortDateString()</td>
                <td>@release.Company</td>
                <td>@release.Title</td>
            </tr>
        }
    </tbody>
</table>
 
<pager pager-model="@Model"></pager>

وقتی سولوشن را اجرا می‌کنیم و به صفحه pager tag helper می‌رویم چیزی شبیه به تصویر زیر را در مرورگر می‌بینیم.

در تصویر بالا صفحه دوم نتایج انتخاب شده است.

نتیجه‌گیری

Tag helperها موارد بسیار خوبی برای ASP.NET Core هستند و این امر برای رسیدگی به آن‌ها در جایی بین extension methodهای HTML helper و نمایش اجزا بسیار خوب است. آن‌ها پیشرفته‌تر از extensionهای HTML helper هستند زیرا آن‌ها تزریق وابستگی فریم‌ورک را پشتیبانی می‌کنند.