Tag-хелпер для постраничной навигации

Данное руководство устарело. Актуальное руководство: Руководство по ASP.NET Core 7

Последнее обновление: 09.12.2019

В прошлой теме был рассмотрен постраничный вывод. Для создания ссылок применялись встроенные хелперы:

@if (Model.PageViewModel.HasPreviousPage)
{
    <a asp-action="Index"
       asp-route-page="@(Model.PageViewModel.PageNumber - 1)"
       class="btn btn-outline-dark">
        <i class="glyphicon glyphicon-chevron-left"></i>
        Назад
    </a>
}
@if (Model.PageViewModel.HasNextPage)
{
    <a asp-action="Index"
       asp-route-page="@(Model.PageViewModel.PageNumber + 1)"
       class="btn btn-outline-dark">
        Вперед
        <i class="glyphicon glyphicon-chevron-right"></i>
    </a>
}

Но, как правило, для создания постраничной навигации создаются какие-то специальные панельки, где можно увидеть ссылки с номерами страниц, по которым можно перейти к нужным страницы, еще какие-то элементы. Все эти элементы нередко должным образом стилизованы.

Нередко элементы навигации - ссылки размещаются в виде ненумерованного списка. В данном случае наша задача - создать компонент навигации наподобие следующего:

<ul class="pagination">
	<li><a href="/?page=1">1</a></li>
	<li class="active"><a>2</a></li>
	<li><a href="/?page=3">3</a></li>
</ul>

Для стилизации применяется класс bootstrap - pagination. Перед активной ссылкой располагаются ссылки на предыдущую и следующую страницу.

Вместо того, чтобы определять все необходимые ссылки и элементы напрямую в представлении, удобнее создать специальный tag-хелпер. Поэтому добавим в проект новую папку TagHelpers, а в эту папку добавим новый класс PageLinkTagHelper:

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.Routing;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Razor.TagHelpers;
using PagerApp.Models;

namespace PagerApp.TagHelpers
{
    public class PageLinkTagHelper : TagHelper
    {
        private IUrlHelperFactory urlHelperFactory;
        public PageLinkTagHelper(IUrlHelperFactory helperFactory)
        {
            urlHelperFactory = helperFactory;
        }
        [ViewContext]
        [HtmlAttributeNotBound]
        public ViewContext ViewContext { get; set; }
        public PageViewModel PageModel { get; set; }
        public string PageAction { get; set; }
        
        public override void Process(TagHelperContext context, TagHelperOutput output)
        {
            IUrlHelper urlHelper = urlHelperFactory.GetUrlHelper(ViewContext);
            output.TagName = "div";

            // набор ссылок будет представлять список ul
            TagBuilder tag = new TagBuilder("ul");
            tag.AddCssClass("pagination");

            // формируем три ссылки - на текущую, предыдущую и следующую
            TagBuilder currentItem = CreateTag(PageModel.PageNumber, urlHelper);

            // создаем ссылку на предыдущую страницу, если она есть
            if (PageModel.HasPreviousPage)
            {
                TagBuilder prevItem = CreateTag(PageModel.PageNumber - 1, urlHelper);
                tag.InnerHtml.AppendHtml(prevItem);
            }
            
            tag.InnerHtml.AppendHtml(currentItem);
            // создаем ссылку на следующую страницу, если она есть
            if (PageModel.HasNextPage)
            {
                TagBuilder nextItem = CreateTag(PageModel.PageNumber + 1, urlHelper);
                tag.InnerHtml.AppendHtml(nextItem);
            }
            output.Content.AppendHtml(tag);
        }

        TagBuilder CreateTag(int pageNumber, IUrlHelper urlHelper)
        {
            TagBuilder item = new TagBuilder("li");
            TagBuilder link = new TagBuilder("a");
            if (pageNumber == this.PageModel.PageNumber)
            {
                item.AddCssClass("active");
            }
            else
            {
                link.Attributes["href"] = urlHelper.Action(PageAction, new { page = pageNumber });
            }
			item.AddCssClass("page-item");
            link.AddCssClass("page-link");
            link.InnerHtml.Append(pageNumber.ToString());
            item.InnerHtml.AppendHtml(link);
            return item;
        }
    }
}

Фреймворк MVC предоставляет ряд сервисов, и один из них - IUrlHelperFactory, который используется для создания ссылки и который мы можем получить в конструкторе.

Всю информацию о пагинации мы получаем через свойство PageModel. Свойство PageAction указывает на метод контроллера, на который будет создаваться ссылка.

Для создания ссылки используется объект IUrlHelper, а для его получения нам нужен контекст представления, в котором вызывается tag-хелпер. Получить контекст представления мы можем через внедрение зависимости через атрибуты. В частности, чтобы получить контекст представления над свойством ставится атрибут ViewContext:

[ViewContext]
[HtmlAttributeNotBound]
public ViewContext ViewContext { get; set; }

Чтобы избежать привязки к атрибутам тега, к свойству также применяется атрибут HtmlAttributeNotBound.

В методе Process вначале получаем объект IUrlHelper для создания ссылки:

IUrlHelper urlHelper = urlHelperFactory.GetUrlHelper(ViewContext);

Далее создаем html-элемент ul. Затем нам надо создать максимум три ссылки, если позволяет количество страниц. Так как каждый элемент списка ul создается одним и тем же способом, то весь механизм создания элемента списка с ссылкой вынесен в отдельный метод CreateTag().

Для стилизации ссылок применяются классы pagination, page-item и page-link фреймворка bootstrap, который подключается в приложении ASP.NET Core MVC по умолчанию.

Применим этот хелпер в представлении:

@using PagerApp.Models
@model IndexViewModel

@*подключаем все tag-хелперы*@
@addTagHelper *, PagerApp
@{
    ViewData["Title"] = "Список пользователей";
}
<h1>Список пользователей</h1>
<table class="table">
    <tr><th>Имя</th><th>Возраст</th><th>Компания</th></tr>
    @foreach (User u in Model.Users)
    {
        <tr><td>@u.Name</td><td>@u.Age</td><td>@u.Company.Name</td></tr>
    }
</table>
<page-link page-model="Model.PageViewModel" page-action="Index"></page-link>

Так как название хелпера состоит из нескольких частей: PageLinkTagHelper, то при использовании все эти части разделяются дефисом (суффикс TagHelper отбрасывается): page-link. То же самое касается и свойств хелпера. Так, чтобы передать значение для свойства PageModel, нам надо использовать атрибут page-model.

Код контроллера остается тем же, что и в прошлой теме:

using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using PagerApp.Models;
using System.Linq;
using System.Threading.Tasks;

namespace PagerApp.Controllers
{
    public class HomeController : Controller
    {
        UsersContext db;
        public HomeController(UsersContext context)
        {
            this.db = context;
        }
        public async Task<IActionResult> Index(int page=1)
        {
            int pageSize = 3;
            IQueryable<User> source = db.Users.Include(x=>x.Company);
            var count = await source.CountAsync();
            var items = await source.Skip((page - 1) * pageSize).Take(pageSize).ToListAsync();

            PageViewModel pageViewModel = new PageViewModel(count, page, pageSize);
            IndexViewModel viewModel = new IndexViewModel
            {
                PageViewModel = pageViewModel,
                Users =items
            };
            return View(viewModel);
        }
    }
}

Итоговый проект будет выглядеть следующим образом:

Pagination in ASP.NET Core MVC

И при запуске проекта мы увидим набор стилизованных ссылок, по котором сможем перемещаться по страницам:

Постраничная навигация в ASP.NET Core
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850