Компоненты представления

Rick Anderson

Компоненты представления

В ASP.NET MVC 6 компоненты представления похожи на частичные представления, но они более мощные. Компоненты представления включают в себя преимущества “разделения ответственности” и тестируемости, которые существуют между контроллером и представлением. Компонент представления отвечает за отображение некой части, а не всего целого. Вы можете использовать компоненты представления, чтобы решить проблему, которую не стоит решать с помощью частичных представлений, например:

  • меню с динамической навигацией
  • облако тэгов (с запросами к БД)
  • панель логина
  • корзина покупателя
  • недавно опубликованные статьи
  • контент сбоку в обычном блоге

Компонент представления можно использовать, чтобы создать панель логина, которая отображается на каждой странице и имеет такой функционал:

  • если пользователь не залогинен, отображается панель логина
  • если пользователь залогинен, отображаются ссылки на выход из аккаунта и управление аккаунтом
  • если пользователь является администратором, отображается панель администратора

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

Компонент представления состоит из двух частей: класса (который обычно наследуется от ViewComponent) и Razor представления, который вызывает методы в классе компонента представления. Как и контроллеры, компоненты представления могут быть POCO, но большинство пользователей захотят получить преимущества от методов и свойств, которые доступны при наследовании от ViewComponent.

Класс компонента представления может быть создан каким-либо из этих способов:

  • наследование от ViewComponent
  • когда классу добавляется атрибут [ViewComponent] или когда класс наследуется от класса с атрибутом [ViewComponent]
  • добавить в конце имени класса ViewComponent.

Как и контроллеры, компоненты представления должны быть открытыми, не вложенными, не абстрактными классами.

Изучение класса ViewComponent

  • Изучите файл src\TodoList\ViewComponents\PriorityListViewComponent.cs:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    using System.Linq;
    using Microsoft.AspNet.Mvc;
    using TodoList.Models;
    
    namespace TodoList.ViewComponents
    {
      public class PriorityListViewComponent : ViewComponent
      {
        private readonly ApplicationDbContext db;
    
        public PriorityListViewComponent(ApplicationDbContext context)
        {
          db = context;
        }
    
        public IViewComponentResult Invoke(int maxPriority)
        {
          var items = db.TodoItems.Where(x => x.IsDone == false &&
              x.Priority <= maxPriority);
    
          return View(items);
        }
      }
    }
    

В коде обратите внимание на:

  • классы компонента представления могут содержаться в любой папке проекта

  • поскольку имя класса PriorityListViewComponent заканчивается на ViewComponent, при рантайме будет использоваться строка “PriorityList”. Позже мы это обсудим более детально.

  • атрибут [ViewComponent] может изменить имя, которое используется для ссылки на компонент представления. Например, мы могли бы назвать класс XYZ и применить атрибут ViewComponent:

    1
    2
    [ViewComponent(Name = "PriorityList")]
    public class XYZ : ViewComponent
    
  • Атрибут [ViewComponent] говорит селектору компонента представлений использовать имя PriorityList, когда он ищет представления, связанные с компонентом, и использовать строку “PriorityList” при ссылке на компонент класса из представления.

  • Компонент использует внедрение конструктора, чтобы данные были доступны.

  • Invoke может быть вызван из представления, и он может принимать произвольное число аргументов. Доступна также асинхронная версия - InvokeAsync. Далее мы рассмотрим InvokeAsync с несколькими аргументами. В коде выше метод Invoke возвращает набор ToDoItems, у которых приоритет выше или равен maxPriority.

Представление компонента представления

  1. Изучите контекст Views\Todo\Components. Эта папка должна быть названа Components.

Примечание

Представления компонента представления обычно добавляются в папку Views\Shared\Components.

  1. Просмотрите папку Views\Todo\Components\PriorityList. Имя этой папки должно соответствовать имени класса компонента представления или имени класса минус суффикс (если мы следовали соглашению и использовали суффикс ViewComponent в имени класса). Если вы использовали атрибут ViewComponent, тогда имя папки должно соответствовать обозначению атрибута.
  2. Просмотрите Razor представление Views\Todo\Components\PriorityList\Default.cshtml.
1
2
3
4
5
6
7
8
9
@model IEnumerable<TodoList.Models.TodoItem>

<h3>Priority Items</h3>
<ul>
  @foreach (var todo in Model)
  {
    <li>@todo.Title</li>
  }
</ul>

Razor принимает список TodoItems и отображает его. Если метод invoke не подходит имени представления (как в нашем примере), по умолчанию для представления используется Default. Далее я покажу вам, как передать имя в представление.

  1. Добавьте div, содержащий вызов приоритетного списка компонентов, в начале файла views\todo\index.cshtml:
1
2
3
4
5
6
7
@* Markup removed for brevity *@
<div>@Html.ActionLink("Create New Todo", "Create", "Todo") </div>
<div>
  <div class="col-md-4">
    @Component.Invoke("PriorityList", 1)
  </div>
</div>

Разметка @Component.Invoke показывает синтаксис для вызова компонентов представления. Первый аргумент - это имя компонента, который мы хотим вызвать. Последующие параметры передаются компоненту. В данном случае мы передаем “1”. Invoke и InvokeAsync могут принимать произвольное число аргументов.

На изображении снизу показан список приоритетных элементов: (убедитесь, что у вас есть, как минимум, один элемент в приоритетом 1)

../_images/pi.png

Добавление InvokeAsync к приоритетному компоненту представления

Обновите класс приоритетного компонента представления следующим кодом:

Примечание

IQueryable отображает пример синхронно, а не асинхронно. Вот пример, как вы можете вызывать асинхронные методы.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
using System.Threading.Tasks;

public class PriorityListViewComponent : ViewComponent
{
  private readonly ApplicationDbContext db;

  public PriorityListViewComponent(ApplicationDbContext context)
  {
    db = context;
  }

  // Synchronous Invoke removed.

  public async Task<IViewComponentResult> InvokeAsync(int maxPriority, bool isDone)
  {
    var items = await GetItemsAsync(maxPriority, isDone);
    return View(items);
  }

  private Task<IQueryable<TodoItem>> GetItemsAsync(int maxPriority, bool isDone)
  {
    return Task.FromResult(GetItems(maxPriority, isDone));
  }
  private IQueryable<TodoItem> GetItems(int maxPriority, bool isDone)
  {
    var items = db.TodoItems.Where(x => x.IsDone == isDone &&
        x.Priority <= maxPriority);

    string msg = "Priority <= " + maxPriority.ToString() +
           " && isDone == " + isDone.ToString();
    ViewBag.PriorityMessage = msg;

    return items;
  }
}

Обновите Razor представление компонента представления (TodoList\src\TodoList\Views\ToDo\Components\PriorityList\Default.cshtml), чтобы показать сообщение:

1
2
3
4
5
6
7
8
9
@model IEnumerable<TodoList.Models.TodoItem>

<h4>@ViewBag.PriorityMessage</h4>
<ul>
  @foreach (var todo in Model)
  {
    <li>@todo.Title</li>
  }
</ul>

Наконец, обновите представление views\todo\index.cshtml:

1
2
3
4
@* Markup removed for brevity. *@
<div class="col-md-4">
    @await Component.InvokeAsync("PriorityList", 2, true)
</div>

На следующем изображении представлены изменения, которые мы сделали в приоритетном компоненте представления и представлении Index:

../_images/p2.png

Определение имени представления

Сложный компонент представления при некоторых условиях должен указать представление не по умолчанию. Вот мы указываем представление “PVC” из метода InvokeAsync: Обновите InvokeAsync в классе PriorityListViewComponent.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
public async Task<IViewComponentResult> InvokeAsync(int maxPriority, bool isDone)
{
  string MyView = "Default";
  // If asking for all completed tasks, render with the "PVC" view.
  if (maxPriority > 3 && isDone == true)
  {
    MyView = "PVC";
  }
  var items = await GetItemsAsync(maxPriority, isDone);
  return View(MyView, items);
}

Изучите представление Views\Todo\Components\PriorityList\PVC.cshtml. Я изменил представление PVC, чтобы проверить, как оно используется:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
@model IEnumerable<TodoList.Models.TodoItem>

<h2> PVC Named Priority Component View</h2>
<h4>@ViewBag.PriorityMessage</h4>
<ul>
  @foreach (var todo in Model)
  {
    <li>@todo.Title</li>
  }
</ul>

Наконец, обновите Views\TodoIndex.cshtml:

1
@await Component.InvokeAsync("PriorityList",  4, true)

Запустите приложение и нажмите на ссылку PVC (или перейдите к :<port>/Todo/IndexFinal). Обновите страницу, чтобы увидеть представление PVC.

../_images/pvc.png
Поделись хорошей новостью с друзьями!
Следи за новостями!