Просмотр компонентов в ASP.NET Core

Автор: Рик Андерсон (Rick Anderson)

Просмотр компонентов

Компоненты представлений похожи на частичные представления, но при этом гораздо функциональнее. Компоненты представления не используют привязку модели. Они зависят от данных, передаваемых при вызове компонента представления. Эта статья написана с помощью контроллеров и представлений, но компоненты представлений работают с Razor Pages.

Компонент представлений:

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

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

  • Меню динамической навигации.
  • Облако тегов, которое запрашивает базу данных.
  • Панель входа.
  • Корзина.
  • Недавно опубликованные статьи.
  • Содержимое боковой панели в блоге.
  • Панель входа, которая будет отображаться на каждой странице и отображать ссылки на выход или вход в зависимости от состояния входа пользователя.

Компонент представления состоит из двух частей:

  • Класс, обычно производный от ViewComponent.
  • Результат, который он обычно возвращает, — это представление.

Как и контроллеры, компонент представления может быть POCO, но большинство разработчиков используют методы и свойства, доступные благодаря наследованию от ViewComponent.

Рекомендуется при рассмотрении того, соответствуют ли компоненты представления спецификациям приложения, использовать компоненты Razor вместо этого. Razor компоненты также объединяют разметку и код C# для создания повторно используемых единиц пользовательского интерфейса. Razor компоненты предназначены для повышения эффективности работы разработчиков за счет обеспечения логики и композиции пользовательского интерфейса на стороне клиента. Дополнительные сведения см. в статье Компоненты Razor ASP.NET Core. Сведения о том, как интегрировать компоненты Razor в приложение MVC или Razor Pages, см. в статье «Интеграция компонентов ASP.NET Core Razor с MVC или Razor Pages».

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

Этот раздел содержит общие требования к созданию компонента представления. Далее в этой статье мы подробно рассмотрим каждый шаг и создадим компонент представления.

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

Любой из следующих элементов может создать класс компонента представления:

  • Производный от ViewComponent.
  • Декорирование класса с использованием атрибута [ViewComponent] или наследование от класса с атрибутом [ViewComponent].
  • Создание класса, в котором имя заканчивается суффиксом ViewComponent.

Как и контроллеры, видовые компоненты должны быть открытыми, невложенными и неабстрактными классами. Имя компонента представления — это имя класса с удаленным суффиксом ViewComponent . Его также можно задать явно с помощью свойства Name.

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

  • Поддерживает внедрение зависимостей конструктора.
  • Не участвует в жизненном цикле контроллера, поэтому фильтры нельзя использовать в компоненте представления.

Чтобы класс с суффиксом ViewComponent, который не учитывает регистр, не рассматривался как компонент представления, пометьте класс атрибутом [NonViewComponent].

using Microsoft.AspNetCore.Mvc;

[NonViewComponent]
public class ReviewComponent
{
    public string Status(string name) => JobStatus.GetCurrentStatus(name);
}

Методы компонентов представлений

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

  • InvokeAsync метод, возвращающий Task<IViewComponentResult>.
  • Invoke синхронный метод, возвращающий объект IViewComponentResult.

Параметры берутся непосредственно из вызова компонента представления, а не из привязки модели. Компонент представления никогда не обрабатывает запрос напрямую. Как правило, компонент представления инициализирует модель и передает ее в представление, вызвав метод View. Вкратце, методы компонентов представления:

  • Определяют метод InvokeAsync, который возвращает Task<IViewComponentResult>, или синхронный метод Invoke, возвращающий IViewComponentResult.
  • Обычно инициализирует модель и передает ее в представление путем вызова метода ViewComponent.View .
  • Параметры приходят из вызывающего метода, а не из HTTP. Это связано с тем, что привязка модели отсутствует.
  • Недоступны напрямую как конечная точка HTTP. Обычно они вызываются в контексте представления. Компонент представления никогда не обрабатывает запрос.
  • Перегружаются по сигнатуре, а не по деталям из текущего HTTP-запроса.

Путь поиска

Среда выполнения ищет представление по следующим путям:

  • /Views/{Имя контроллера}/Components/{Имя компонента представления}/{Имя представления}
  • /Views/Shared/Components/{Имя компонента представления}/{Имя представления}
  • /Pages/Shared/Components/{Имя компонента представления}/{Имя представления}
  • /Области/{Имя области}/Views/Shared/Components/{View Component Name}/{View Name}

Путь поиска применяется к проектам с контроллерами, представлениями и Razor страницами.

Имя представления по умолчанию для компонента представления — Defaultэто означает, что файлы представления обычно будут называться Default.cshtml. При создании результата компонента представления или при вызове View метода можно указать другое имя представления.

Рекомендуется называть файл представления как Default.cshtml и использовать путь Views/Shared/Components/{View Component Name}/{View Name}. Компонент представления, используемый PriorityList в этом примере, используется Views/Shared/Components/PriorityList/Default.cshtml для представления компонента представления.

Настраивайте путь поиска представления

Чтобы настроить путь поиска представления, измените коллекцию RazorViewLocationFormats. Например, чтобы найти представления в пути /Components/{View Component Name}/{View Name}, добавьте новый элемент в коллекцию:

using Microsoft.EntityFrameworkCore;
using ViewComponentSample.Models;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllersWithViews()
    .AddRazorOptions(options =>
    {
        options.ViewLocationFormats.Add("/{0}.cshtml");
    });

builder.Services.AddDbContext<ToDoContext>(options =>
        options.UseInMemoryDatabase("db"));

var app = builder.Build();

// Remaining code removed for brevity.

В предыдущем коде заполнитель {0} представляет путь Components/{View Component Name}/{View Name}.

Вызов компонента представления

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

@await Component.InvokeAsync("Name of view component",
                             {Anonymous Type Containing Parameters})

Параметры передаются методу InvokeAsync . Компонент PriorityList представления, разработанный в статье, вызывается из Views/ToDo/Index.cshtml файла представления. В следующем коде InvokeAsync метод вызывается с двумя параметрами:

</table>

<div>
    Maximum Priority: @ViewData["maxPriority"] <br />
    Is Complete:  @ViewData["isDone"]
    @await Component.InvokeAsync("PriorityList",
                     new { 
                         maxPriority =  ViewData["maxPriority"],
                         isDone = ViewData["isDone"]  }
                     )
</div>

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

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

<div>
       Maxium Priority: @ViewData["maxPriority"] <br />
       Is Complete:  @ViewData["isDone"]
    @{
        int maxPriority = Convert.ToInt32(ViewData["maxPriority"]);
        bool isDone = Convert.ToBoolean(ViewData["isDone"]);
    }
    <vc:priority-list max-priority=maxPriority is-done=isDone>
    </vc:priority-list>
</div>

Параметры методов и классов, указанные в стиле Pascal, для вспомогательных функций тегов преобразуются в кебаб-стиль. Вспомогательная функция тегов для вызова компонента представления использует элемент <vc></vc>. Компонент представления задан следующим образом:

<vc:[view-component-name]
  parameter1="parameter1 value"
  parameter2="parameter2 value">
</vc:[view-component-name]>

Чтобы использовать компонент представления в качестве вспомогательного приложения тегов, зарегистрируйте с помощью директивы @addTagHelperсборку, содержащую этот компонент представления. Если компонент представления находится в сборке MyWebApp, добавьте следующую директиву в _ViewImports.cshtml файл:

@addTagHelper *, MyWebApp

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

Метод InvokeAsync, используемый в этом учебнике:

@await Component.InvokeAsync("PriorityList",
                 new { 
                     maxPriority =  ViewData["maxPriority"],
                     isDone = ViewData["isDone"]  }
                 )

В предыдущей разметке компонент представления PriorityList становится priority-list. Параметры передаются в компонент представления как атрибуты в кебаб-стиле.

Вызов компонента представления непосредственно с контроллера

Компоненты представления обычно вызываются из представления, но их можно вызывать непосредственно из метода контроллера. Хотя компоненты представления не определяют конечные точки, такие как контроллеры, действие контроллера, возвращающее содержимое ViewComponentResult можно реализовать.

В следующем примере компонент представления вызывается непосредственно из контроллера:

public IActionResult IndexVC(int maxPriority = 2, bool isDone = false)
{
    return ViewComponent("PriorityList",
        new { 
           maxPriority = maxPriority,
           isDone = isDone
        });
}

Создание базового компонента представления

Скачайте, выполните сборку и проверьте начальный код. Это базовый проект с контроллером ToDo , который отображает список элементов ToDo .

Список дел

Обновите контроллер, чтобы передать приоритет и статус завершения

Index Обновите метод для использования параметров состояния приоритета и завершения:

using Microsoft.AspNetCore.Mvc;
using ViewComponentSample.Models;

namespace ViewComponentSample.Controllers;
public class ToDoController : Controller
{
    private readonly ToDoContext _ToDoContext;

    public ToDoController(ToDoContext context)
    {
        _ToDoContext = context;
        _ToDoContext.Database.EnsureCreated();
    }

    public IActionResult Index(int maxPriority = 2, bool isDone = false)
    {
        var model = _ToDoContext!.ToDo!.ToList();
        ViewData["maxPriority"] = maxPriority;
        ViewData["isDone"] = isDone;
        return View(model);
    }

Добавление класса ViewComponent

Добавление класса ViewComponent в ViewComponents/PriorityListViewComponent.cs:

using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using ViewComponentSample.Models;

namespace ViewComponentSample.ViewComponents;

public class PriorityListViewComponent : ViewComponent
{
    private readonly ToDoContext db;

    public PriorityListViewComponent(ToDoContext context) => db = context;

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

    private Task<List<TodoItem>> GetItemsAsync(int maxPriority, bool isDone)
    {
        return db!.ToDo!.Where(x => x.IsDone == isDone &&
                             x.Priority <= maxPriority).ToListAsync();
    }
}

Примечания к коду:

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

  • Так как имя класса PriorityListViewComponent заканчивается суффиксом ViewComponent, среда выполнения использует строку PriorityList при ссылке на компонент класса из представления.

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

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

    • Название PriorityList используется при поиске представлений, связанных с компонентом.
    • Строка PriorityList при ссылке на компонент класса из представления.
  • Компонент использует внедрение зависимостей для предоставления контекста данных.

  • InvokeAsync предоставляет метод, который можно вызвать из представления, и может принимать произвольное количество аргументов.

  • Метод InvokeAsync возвращает набор элементов ToDo, удовлетворяющих параметрам isDone и maxPriority.

Создайте компонент представления Razor

  • Создайте папку Views/Shared/Components. Она должна называться Components.

  • Создайте папку Views/Shared/Components/PriorityList. Это имя папки должно совпадать с именем класса компонента представления или именем класса минус суффикс. ViewComponent Если используется атрибут, имя класса потребуется сопоставить с обозначением атрибута.

  • Views/Shared/Components/PriorityList/Default.cshtml Razor Создайте представление:

    @model IEnumerable<ViewComponentSample.Models.TodoItem>
    
    <h3>Priority Items</h3>
    <ul>
        @foreach (var todo in Model)
        {
            <li>@todo.Name</li>
        }
    </ul>
    

    Представление Razor принимает список TodoItem и отображает их. Если метод компонента InvokeAsync представления не передает имя представления, значение по умолчанию используется для имени представления по соглашению . Чтобы переопределить стиль по умолчанию для определенного контроллера, добавьте представление в папку соответствующего контроллера (например, Views/ToDo/Components/PriorityList/Default.cshtml).

    Если компонент представления зависит от контроллера, его можно добавить в папку для конкретного контроллера. Например, Views/ToDo/Components/PriorityList/Default.cshtml зависит от контроллера.

  • Добавьте div, содержащий вызов компонента списка приоритетов, в конец файла Views/ToDo/index.cshtml.

    </table>
    
    <div>
        Maximum Priority: @ViewData["maxPriority"] <br />
        Is Complete:  @ViewData["isDone"]
        @await Component.InvokeAsync("PriorityList",
                         new { 
                             maxPriority =  ViewData["maxPriority"],
                             isDone = ViewData["isDone"]  }
                         )
    </div>
    

Разметка @await Component.InvokeAsync показывает синтаксис для вызова компонентов представления. Первым аргументом является имя компонента, который требуется вызвать. Последующие параметры передаются в компонент. InvokeAsync может занять произвольное число аргументов.

Тестирование приложения. Следующий рисунок показывает список дел и элементы с приоритетом:

список дел и элементы с приоритетом

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

public IActionResult IndexVC(int maxPriority = 2, bool isDone = false)
{
    return ViewComponent("PriorityList",
        new { 
           maxPriority = maxPriority,
           isDone = isDone
        });
}

элементы с приоритетом из действия IndexVC

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

Сложный компонент вида в определённых условиях может потребовать указать нестандартное представление. Следующий код показывает, как из метода InvokeAsync указать представление "PVC". Обновите метод InvokeAsync в классе PriorityListViewComponent.

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/Shared/Components/PriorityList/Default.cshtml в представление с именем Views/Shared/Components/PriorityList/PVC.cshtml. Добавьте заголовок, чтобы указать используемое представление PVC.

@model IEnumerable<ViewComponentSample.Models.TodoItem>

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

Запустите приложение и проверьте представление PVC.

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

Если вид PVC не отрисовывается, убедитесь, что вызывается компонент вида с приоритетом 4 или выше.

Изучение пути к представлению

  • Задайте для параметра приоритета значение 3 или меньше, чтобы представление с приоритетом не возвращалось.

  • Временно переименуйте Views/ToDo/Components/PriorityList/Default.cshtml в 1Default.cshtml.

  • Протестируйте приложение, возникает следующая ошибка:

    An unhandled exception occurred while processing the request.
    InvalidOperationException: The view 'Components/PriorityList/Default' wasn't found. The following locations were searched:
    /Views/ToDo/Components/PriorityList/Default.cshtml
    /Views/Shared/Components/PriorityList/Default.cshtml
    
  • Скопируйте Views/ToDo/Components/PriorityList/1Default.cshtml в Views/Shared/Components/PriorityList/Default.cshtml.

  • Добавьте разметку в компонент представления Shared ToDo, чтобы указать, что это представление из папки Shared.

  • Протестируйте представление компонента Shared.

Показ задач ToDo с общим видом компонента

Избегайте жестко закодированных строк

Для обеспечения безопасности во времени компиляции замените жестко закодированное имя компонента представления именем класса. Обновите файл PriorityListViewComponent.cs, чтобы не использовать суффикс ViewComponent:

using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using ViewComponentSample.Models;

namespace ViewComponentSample.ViewComponents;

public class PriorityList : ViewComponent
{
    private readonly ToDoContext db;

    public PriorityList(ToDoContext context)
    {
        db = context;
    }

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

    private Task<List<TodoItem>> GetItemsAsync(int maxPriority, bool isDone)
    {
        return db!.ToDo!.Where(x => x.IsDone == isDone &&
                             x.Priority <= maxPriority).ToListAsync();
    }
}

Файл представления :

</table>

<div>
    Testing nameof(PriorityList) <br />

    Maxium Priority: @ViewData["maxPriority"] <br />
    Is Complete:  @ViewData["isDone"]
    @await Component.InvokeAsync(nameof(PriorityList),
                     new { 
                         maxPriority =  ViewData["maxPriority"],
                         isDone = ViewData["isDone"]  }
                     )
</div>

Перегрузка Component.InvokeAsync метода, принимающего тип CLR, использует typeof оператор:

</table>

<div>
    Testing typeof(PriorityList) <br />

    Maxium Priority: @ViewData["maxPriority"] <br />
    Is Complete:  @ViewData["isDone"]
    @await Component.InvokeAsync(typeof(PriorityList),
                     new { 
                         maxPriority =  ViewData["maxPriority"],
                         isDone = ViewData["isDone"]  }
                     )
</div>

Выполнение синхронных задач

Платформа обрабатывает вызов синхронного Invoke метода, если асинхронная работа не требуется. Следующий метод создает синхронный компонент представления Invoke:

using Microsoft.AspNetCore.Mvc;
using ViewComponentSample.Models;

namespace ViewComponentSample.ViewComponents
{
    public class PriorityListSync : ViewComponent
    {
        private readonly ToDoContext db;

        public PriorityListSync(ToDoContext context)
        {
            db = context;
        }

        public IViewComponentResult Invoke(int maxPriority, bool isDone)
        {
 
            var x = db!.ToDo!.Where(x => x.IsDone == isDone &&
                                  x.Priority <= maxPriority).ToList();
            return View(x);
        }
    }
}

Файл компонента представления Razor:

<div>
    Testing nameof(PriorityList) <br />

    Maxium Priority: @ViewData["maxPriority"] <br />
    Is Complete:  @ViewData["isDone"]
    @await Component.InvokeAsync(nameof(PriorityListSync),
                     new { 
                         maxPriority =  ViewData["maxPriority"],
                         isDone = ViewData["isDone"]  }
                     )
</div>

Компонент представления вызывается в Razor файле (например, Views/Home/Index.cshtmlс помощью одного из следующих подходов:

Чтобы использовать подход IViewComponentHelper, вызовите Component.InvokeAsync:

@await Component.InvokeAsync(nameof(PriorityList),
                             new { maxPriority = 4, isDone = true })

Чтобы использовать вспомогательное приложение тэгов зарегистрируйте с помощью директивы @addTagHelperсборку, содержащую этот компонент представления (он находится в сборке с именем MyWebApp):

@addTagHelper *, MyWebApp

Используйте вспомогательный элемент тега компонента представления в файле разметки Razor :

<vc:priority-list max-priority="999" is-done="false">
</vc:priority-list>

Сигнатура PriorityList.Invoke метода синхронна, но Razor находит и вызывает метод в Component.InvokeAsync файле разметки.

Дополнительные ресурсы

Просмотр компонентов

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

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

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

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

  • Динамические меню навигации
  • Облако тегов, которое выполняет запрос к базе данных
  • Панель входа
  • Корзина покупок
  • Недавно опубликованные статьи
  • Содержимое боковой панели в блоге
  • Панель входа, отображаемая на каждой странице и отображающая ссылки для выхода или входа в зависимости от состояния входа пользователя

Компонент представления состоит из двух частей:

  • Класс, как правило, производный от ViewComponent
  • Результат, который он возвращает, как правило, это например, представление.

Как и контроллеры, компонент представления может быть POCO, но большинство разработчиков пользуются методами и свойствами, доступными при наследовании от ViewComponent.

При рассмотрении того, соответствуют ли компоненты представления спецификациям приложения, рассмотрите использование компонентов Razor вместо этого. Razor компоненты также объединяют разметку с кодом C# для создания повторно используемых единиц пользовательского интерфейса. Razor компоненты разработаны для повышения эффективности разработчиков в процессе предоставления клиентской логики пользовательского интерфейса и композиции. Дополнительные сведения см. в статье Компоненты Razor ASP.NET Core. Сведения о том, как интегрировать компоненты Razor в приложение MVC или Razor Pages, см. в разделе Интеграция компонентов ASP.NET Core Razor с MVC или Razor Pages.

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

Этот раздел содержит общие требования к созданию компонента представления. Далее в этой статье мы подробно рассмотрим каждый шаг и создадим компонент представления.

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

Класс компонентов представлений можно создать одним из следующих способов:

  • Производный от ViewComponent
  • Декорирование класса с помощью атрибута [ViewComponent] или наследование от класса с помощью атрибута [ViewComponent]
  • Создание класса, в котором имя заканчивается суффиксом ViewComponent

Как и контроллеры, компоненты представлений должны быть открытыми, невложенными и неабстрактными классами. Имя компонента представления — это имя класса с удаленным суффиксом ViewComponent . Его можно задать явно с помощью свойства Name.

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

  • Поддерживает внедрение зависимостей через конструктор
  • Не участвует в жизненном цикле контроллера, поэтому фильтры нельзя использовать в компоненте представления.

Чтобы запретить классу, который имеет суффикс ViewComponent без учета регистра, рассматриваться как компонент представления, украшайте класс атрибутом [NonViewComponent] :

using Microsoft.AspNetCore.Mvc;

[NonViewComponent]
public class ReviewComponent
{
    public string Status(string name) => JobStatus.GetCurrentStatus(name);
}

Методы компонентов представлений

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

  • InvokeAsync метод, возвращающий Task<IViewComponentResult>.
  • Invoke синхронный метод, возвращающий объект IViewComponentResult.

Параметры берутся непосредственно из вызова компонента представления, а не из привязки модели. Компонент представления никогда не обрабатывает запрос напрямую. Как правило, компонент представления инициализирует модель и передает ее в представление, вызвав метод View. Вкратце, методы компонентов представления:

  • Определяют метод InvokeAsync, который возвращает Task<IViewComponentResult>, или синхронный метод Invoke, возвращающий IViewComponentResult.
  • Обычно инициализирует модель и передает ее в представление путем вызова метода ViewComponent.View .
  • Параметры берутся из вызывающего метода, а не из HTTP. Это связано с тем, что привязка модели отсутствует.
  • Недоступны напрямую как конечная точка HTTP. Обычно они вызываются в представлении. Компонент представления никогда не обрабатывает запрос.
  • Перегружаются по сигнатуре, а не по сведениям из текущего HTTP-запроса.

Путь поиска

Среда выполнения ищет представление по следующим путям:

  • /Views/{Имя контроллера}/Components/{Имя компонента представления}/{Имя представления}
  • /Views/Shared/Components/{Имя компонента представления}/{Имя представления}
  • /Pages/Shared/Components/{Имя компонента представления}/{Имя представления}
  • /Areas/{Имя области}/Views/Shared/Components/{View Component Name}/{View Name}

Путь поиска применяется к проектам с использованием контроллеров и представлений, а также Razor Страниц.

Имя представления по умолчанию для компонента представления — Defaultэто означает, что файлы представления обычно будут называться Default.cshtml. При создании результата компонента представления или при вызове View метода можно указать другое имя представления.

Рекомендуется использовать имя файла представления Default.cshtml и путь Views/Shared/Components/{View Component Name}/{View Name}. Компонент представления, используемый PriorityList в этом примере, используется Views/Shared/Components/PriorityList/Default.cshtml для представления компонента представления.

Настройка пути поиска представления

Чтобы настроить путь поиска представления, измените коллекцию RazorViewLocationFormats. Например, чтобы найти представления в пути /Components/{View Component Name}/{View Name}, добавьте новый элемент в коллекцию:

using Microsoft.EntityFrameworkCore;
using ViewComponentSample.Models;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllersWithViews()
    .AddRazorOptions(options =>
    {
        options.ViewLocationFormats.Add("/{0}.cshtml");
    });

builder.Services.AddDbContext<ToDoContext>(options =>
        options.UseInMemoryDatabase("db"));

var app = builder.Build();

// Remaining code removed for brevity.

В предыдущем коде заполнитель {0} представляет путь Components/{View Component Name}/{View Name}.

Вызов компонента представления

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

@await Component.InvokeAsync("Name of view component",
                             {Anonymous Type Containing Parameters})

Параметры передаются методу InvokeAsync . Компонент PriorityList представления, разработанный в статье, вызывается из Views/ToDo/Index.cshtml файла представления. В следующем коде InvokeAsync метод вызывается с двумя параметрами:

</table>

<div>
    Maximum Priority: @ViewData["maxPriority"] <br />
    Is Complete:  @ViewData["isDone"]
    @await Component.InvokeAsync("PriorityList",
                     new { 
                         maxPriority =  ViewData["maxPriority"],
                         isDone = ViewData["isDone"]  }
                     )
</div>

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

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

<div>
       Maxium Priority: @ViewData["maxPriority"] <br />
       Is Complete:  @ViewData["isDone"]
    @{
        int maxPriority = Convert.ToInt32(ViewData["maxPriority"]);
        bool isDone = Convert.ToBoolean(ViewData["isDone"]);
    }
    <vc:priority-list max-priority=maxPriority is-done=isDone>
    </vc:priority-list>
</div>

Параметры методов и классов, указанные в стиле Pascal, для вспомогательных функций тегов преобразуются в кебаб-стиль. Вспомогательная функция тегов для вызова компонента представления использует элемент <vc></vc>. Компонент представления задан следующим образом:

<vc:[view-component-name]
  parameter1="parameter1 value"
  parameter2="parameter2 value">
</vc:[view-component-name]>

Чтобы использовать компонент представления в качестве вспомогательного приложения тегов, зарегистрируйте с помощью директивы @addTagHelperсборку, содержащую этот компонент представления. Если компонент представления находится в сборке MyWebApp, добавьте следующую директиву в _ViewImports.cshtml файл:

@addTagHelper *, MyWebApp

Компонент представления можно зарегистрировать как Тег-хелпер для любого файла, который ссылается на компонент представления. Раздел Управление областью вспомогательной функции тегов содержит дополнительные сведения о том, как зарегистрировать вспомогательные функции тегов.

Метод InvokeAsync, используемый в этом учебнике:

@await Component.InvokeAsync("PriorityList",
                 new { 
                     maxPriority =  ViewData["maxPriority"],
                     isDone = ViewData["isDone"]  }
                 )

В предыдущей разметке компонент представления PriorityList становится priority-list. Параметры передаются в компонент представления как атрибуты в стиле кебаб-кейс.

Вызов компонента представления непосредственно с контроллера

Компоненты представления обычно вызываются из представления, но их можно вызывать непосредственно из метода контроллера. Хотя компоненты представления не определяют конечные точки, такие как контроллеры, действие контроллера, возвращающее содержимое ViewComponentResult можно реализовать.

В следующем примере компонент представления вызывается непосредственно из контроллера:

public IActionResult IndexVC(int maxPriority = 2, bool isDone = false)
{
    return ViewComponent("PriorityList",
        new { 
           maxPriority = maxPriority,
           isDone = isDone
        });
}

Создание базового компонента представления

Скачайте начальный код и выполните его сборку и тестирование. Это базовый проект с контроллером ToDo , который отображает список элементов ToDo .

Список дел

Обновите контроллер, чтобы передать приоритет и статус завершения

Index Обновите метод для использования параметров состояния приоритета и завершения:

using Microsoft.AspNetCore.Mvc;
using ViewComponentSample.Models;

namespace ViewComponentSample.Controllers;
public class ToDoController : Controller
{
    private readonly ToDoContext _ToDoContext;

    public ToDoController(ToDoContext context)
    {
        _ToDoContext = context;
        _ToDoContext.Database.EnsureCreated();
    }

    public IActionResult Index(int maxPriority = 2, bool isDone = false)
    {
        var model = _ToDoContext!.ToDo!.ToList();
        ViewData["maxPriority"] = maxPriority;
        ViewData["isDone"] = isDone;
        return View(model);
    }

Добавление класса ViewComponent

Добавление класса ViewComponent в ViewComponents/PriorityListViewComponent.cs:

using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using ViewComponentSample.Models;

namespace ViewComponentSample.ViewComponents;

public class PriorityListViewComponent : ViewComponent
{
    private readonly ToDoContext db;

    public PriorityListViewComponent(ToDoContext context) => db = context;

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

    private Task<List<TodoItem>> GetItemsAsync(int maxPriority, bool isDone)
    {
        return db!.ToDo!.Where(x => x.IsDone == isDone &&
                             x.Priority <= maxPriority).ToListAsync();
    }
}

Примечания к коду:

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

  • Так как имя класса PriorityListViewComponent заканчивается суффиксом ViewComponent, среда выполнения использует строку PriorityList при ссылке на компонент класса из представления.

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

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

    • Название PriorityList, используемое при поиске представлений, связанных с компонентом
    • Строка PriorityList при ссылке на компонент класса из представления.
  • Компонент использует внедрение зависимостей для предоставления контекста данных.

  • InvokeAsync предоставляет метод, который можно вызвать из представления, и может принимать произвольное количество аргументов.

  • Метод InvokeAsync возвращает набор элементов ToDo, удовлетворяющих параметрам isDone и maxPriority.

Создайте компонент представления Razor

  • Создайте папку Views/Shared/Components. Она должна называться Components.

  • Создайте папку Views/Shared/Components/PriorityList. Это имя папки должно совпадать с именем класса компонента представления или именем класса минус суффикс. ViewComponent Если используется атрибут, имя класса потребуется сопоставить с обозначением атрибута.

  • Views/Shared/Components/PriorityList/Default.cshtml Razor Создайте представление:

    @model IEnumerable<ViewComponentSample.Models.TodoItem>
    
    <h3>Priority Items</h3>
    <ul>
        @foreach (var todo in Model)
        {
            <li>@todo.Name</li>
        }
    </ul>
    

    Представление Razor принимает список TodoItem и отображает их. Если метод компонента InvokeAsync представления не передает имя представления, значение по умолчанию используется для имени представления по соглашению . Чтобы переопределить стиль по умолчанию для определенного контроллера, добавьте представление в папку соответствующего контроллера (например, Views/ToDo/Components/PriorityList/Default.cshtml).

    Если компонент представления зависит от контроллера, его можно добавить в папку для конкретного контроллера. Например, Views/ToDo/Components/PriorityList/Default.cshtml зависит от контроллера.

  • Добавьте div, содержащий вызов компонента списка приоритетов, в конец файла Views/ToDo/index.cshtml.

    </table>
    
    <div>
        Maximum Priority: @ViewData["maxPriority"] <br />
        Is Complete:  @ViewData["isDone"]
        @await Component.InvokeAsync("PriorityList",
                         new { 
                             maxPriority =  ViewData["maxPriority"],
                             isDone = ViewData["isDone"]  }
                         )
    </div>
    

Разметка @await Component.InvokeAsync показывает синтаксис для вызова компонентов представления. Первым аргументом является имя компонента, который требуется вызвать. Последующие параметры передаются в компонент. InvokeAsync может занять произвольное число аргументов.

Тестирование приложения. Следующий рисунок показывает список дел и элементы с приоритетом:

список дел и элементы с приоритетом

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

public IActionResult IndexVC(int maxPriority = 2, bool isDone = false)
{
    return ViewComponent("PriorityList",
        new { 
           maxPriority = maxPriority,
           isDone = isDone
        });
}

элементы с приоритетом из действия IndexVC

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

Сложный компонент представления в некоторых условиях может потребовать указать альтернативное представление. Из следующего кода видно, как указать представление "PVC" из метода InvokeAsync. Обновите метод InvokeAsync в классе PriorityListViewComponent.

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/Shared/Components/PriorityList/Default.cshtml в представление под названием Views/Shared/Components/PriorityList/PVC.cshtml. Добавьте заголовок, чтобы указать, что используется представление PVC.

@model IEnumerable<ViewComponentSample.Models.TodoItem>

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

Запустите приложение и проверьте представление PVC.

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

Если представление ПВХ не отрисовывается, убедитесь, что компонент представления с приоритетом 4 или выше вызывается.

Изучение пути к представлению

  • Задайте для параметра приоритета значение 3 или меньше, чтобы представление с приоритетом не возвращалось.

  • Временно переименуйте Views/ToDo/Components/PriorityList/Default.cshtml в 1Default.cshtml.

  • Протестируйте приложение, возникает следующая ошибка:

    An unhandled exception occurred while processing the request.
    InvalidOperationException: The view 'Components/PriorityList/Default' wasn't found. The following locations were searched:
    /Views/ToDo/Components/PriorityList/Default.cshtml
    /Views/Shared/Components/PriorityList/Default.cshtml
    
  • Скопируйте Views/ToDo/Components/PriorityList/1Default.cshtml в Views/Shared/Components/PriorityList/Default.cshtml.

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

  • Протестируйте представление компонента Shared.

Вывод задач с представлением компонента Shared

Избегайте жестко закодированных строк

Для обеспечения безопасности во времени компиляции замените жестко закодированное имя компонента представления именем класса. Обновите файл PriorityListViewComponent.cs, чтобы не использовать суффикс ViewComponent:

using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using ViewComponentSample.Models;

namespace ViewComponentSample.ViewComponents;

public class PriorityList : ViewComponent
{
    private readonly ToDoContext db;

    public PriorityList(ToDoContext context)
    {
        db = context;
    }

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

    private Task<List<TodoItem>> GetItemsAsync(int maxPriority, bool isDone)
    {
        return db!.ToDo!.Where(x => x.IsDone == isDone &&
                             x.Priority <= maxPriority).ToListAsync();
    }
}

Файл представления :

</table>

<div>
    Testing nameof(PriorityList) <br />

    Maxium Priority: @ViewData["maxPriority"] <br />
    Is Complete:  @ViewData["isDone"]
    @await Component.InvokeAsync(nameof(PriorityList),
                     new { 
                         maxPriority =  ViewData["maxPriority"],
                         isDone = ViewData["isDone"]  }
                     )
</div>

Перегрузка метода Component.InvokeAsync, которая принимает тип CLR, использует оператор typeof.

</table>

<div>
    Testing typeof(PriorityList) <br />

    Maxium Priority: @ViewData["maxPriority"] <br />
    Is Complete:  @ViewData["isDone"]
    @await Component.InvokeAsync(typeof(PriorityList),
                     new { 
                         maxPriority =  ViewData["maxPriority"],
                         isDone = ViewData["isDone"]  }
                     )
</div>

Выполнение синхронных задач

Платформа обрабатывает вызов синхронного Invoke метода, если асинхронная работа не требуется. Следующий метод создает синхронный компонент представления Invoke:

using Microsoft.AspNetCore.Mvc;
using ViewComponentSample.Models;

namespace ViewComponentSample.ViewComponents
{
    public class PriorityListSync : ViewComponent
    {
        private readonly ToDoContext db;

        public PriorityListSync(ToDoContext context)
        {
            db = context;
        }

        public IViewComponentResult Invoke(int maxPriority, bool isDone)
        {
 
            var x = db!.ToDo!.Where(x => x.IsDone == isDone &&
                                  x.Priority <= maxPriority).ToList();
            return View(x);
        }
    }
}

Файл компонента Razor представления:

<div>
    Testing nameof(PriorityList) <br />

    Maxium Priority: @ViewData["maxPriority"] <br />
    Is Complete:  @ViewData["isDone"]
    @await Component.InvokeAsync(nameof(PriorityListSync),
                     new { 
                         maxPriority =  ViewData["maxPriority"],
                         isDone = ViewData["isDone"]  }
                     )
</div>

Компонент представления вызывается в Razor файле (например, Views/Home/Index.cshtmlс помощью одного из следующих подходов:

Чтобы использовать подход IViewComponentHelper, вызовите Component.InvokeAsync:

@await Component.InvokeAsync(nameof(PriorityList),
                             new { maxPriority = 4, isDone = true })

Чтобы использовать вспомогательное приложение тэгов зарегистрируйте с помощью директивы @addTagHelperсборку, содержащую этот компонент представления (он находится в сборке с именем MyWebApp):

@addTagHelper *, MyWebApp

Используйте вспомогательный элемент тега компонента представления в файле разметки Razor :

<vc:priority-list max-priority="999" is-done="false">
</vc:priority-list>

Сигнатура PriorityList.Invoke метода синхронна, но Razor находит и вызывает метод в Component.InvokeAsync файле разметки.

Дополнительные ресурсы

Просмотреть или скачать образец кода (описание загрузки)

Просмотр компонентов

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

Компонент представлений:

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

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

  • Динамические меню навигации
  • Облако тегов (где выполняется запрос к базе данных)
  • Панель входа
  • Корзина
  • Недавно опубликованные статьи
  • Содержимое боковой панели в типичном блоге
  • Панель входа, которая должна отображаться на каждой странице и содержит ссылки для выхода или входа в зависимости от состояния входа пользователя

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

Рекомендуется при рассмотрении того, соответствуют ли компоненты представления спецификациям приложения, использовать компоненты Razor вместо этого. Razor компоненты также объединяют разметку и код C# для создания повторно используемых единиц пользовательского интерфейса. Razor компоненты предназначены для повышения эффективности работы разработчиков за счет обеспечения логики и композиции пользовательского интерфейса на стороне клиента. Дополнительные сведения см. в статье Компоненты Razor ASP.NET Core. Сведения о том, как интегрировать компоненты Razor в приложение MVC или Razor Pages, см. в статье «Интеграция компонентов ASP.NET Core Razor с MVC или Razor Pages».

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

Этот раздел содержит общие требования к созданию компонента представления. Далее в этой статье мы подробно рассмотрим каждый шаг и создадим компонент представления.

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

Класс визуального компонента можно создать одним из следующих способов:

  • Наследование от ViewComponent
  • Декорирование класса с помощью атрибута [ViewComponent] или наследование от класса с помощью атрибута [ViewComponent]
  • Создание класса, где имя заканчивается суффиксом ViewComponent

Как и контроллеры, видовые компоненты должны быть открытыми, невложенными и неабстрактными классами. Имя компонента представления — это имя класса с удаленным суффиксом "ViewComponent". Его также можно задать явно с помощью свойства ViewComponentAttribute.Name.

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

  • Полностью поддерживает внедрение зависимостей в конструкторе.
  • Не участвует в жизненном цикле контроллера, поэтому в компоненте представления невозможно использовать фильтры.

Чтобы остановить класс с нечувствительным суффиксом ViewComponent от обработки как компонента представления, украшайте класс атрибутом [NonViewComponent] :

[NonViewComponent]
public class ReviewComponent
{
    // ...

Методы компонентов представлений

Компонент представления задает свою логику в методе InvokeAsync, возвращающем Task<IViewComponentResult>, или в синхронном методе Invoke, который возвращает IViewComponentResult. Параметры берутся непосредственно из вызова компонента представления, а не из привязки модели. Компонент представления никогда не обрабатывает запрос напрямую. Как правило, компонент представления инициализирует модель и передает ее в представление, вызвав метод View. Вкратце, методы компонент представления:

  • Определяют метод InvokeAsync, который возвращает Task<IViewComponentResult>, или синхронный метод Invoke, возвращающий IViewComponentResult.
  • Обычно инициализируют модель и передают ее в представление, вызвав метод ViewComponentView.
  • Параметры поступают из вызывающего метода, а не из HTTP. Это связано с тем, что привязка модели отсутствует.
  • Недоступны непосредственно в качестве конечной точки HTTP. Они вызываются из вашего кода (обычно в представлении). Компонент представления никогда не обрабатывает запрос.
  • Перегружаются по сигнатуре, а не по сведениям из текущего HTTP-запроса.

Просмотр пути поиска

Среда выполнения ищет представление по следующим путям:

  • /Views/{Имя контроллера}/Components/{Имя компонента представления}/{Имя представления}
  • /Views/Shared/Components/{Имя компонента представления}/{Имя представления}
  • /Pages/Shared/Components/{Имя компонента представления}/{Имя представления}
  • /Области/{Имя области}/Views/Shared/Components/{View Component Name}/{View Name}

Путь поиска применяется к проектам с контроллерами, представлениями и Razor страницами.

Имя представления по умолчанию для компонента представления — Default, что означает, что файл представления обычно будет называться Default.cshtml. При создании результата компонента представления или при вызове метода View можно указать другое имя представления.

Мы рекомендуем присвоить файлу представления имя Default.cshtml и использовать путь Views/Shared/Components/{View Component Name}/{View Name}. Компонент представления, используемый PriorityList в этом примере, используется Views/Shared/Components/PriorityList/Default.cshtml для представления компонента представления.

Настраивайте путь поиска представления

Чтобы настроить путь поиска представления, измените коллекцию RazorViewLocationFormats. Например, чтобы найти представления в пути "/Components/{имя компонента представления}/{имя представления}", добавьте в коллекцию новый элемент:

services.AddMvc()
    .AddRazorOptions(options =>
    {
        options.ViewLocationFormats.Add("/{0}.cshtml");
    })
    .SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

В представленном коде заполнитель "{0}" обозначает путь "Components/{Имя компонента представления}/{Имя представления}".

Вызов компонента представления

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

@await Component.InvokeAsync("Name of view component", {Anonymous Type Containing Parameters})

Эти параметры передаются в метод InvokeAsync. Компонент PriorityList представления, разработанный в статье, вызывается из Views/ToDo/Index.cshtml файла представления. Следующий код вызывает метод InvokeAsync с двумя параметрами:

@await Component.InvokeAsync("PriorityList", new { maxPriority = 4, isDone = true })

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

Для ASP.NET Core 1.1 и более поздних версий можно вызвать компонент представления в качестве вспомогательной функции тегов:

<vc:priority-list max-priority="2" is-done="false">
</vc:priority-list>

Параметры методов и классов, указанные в стиле Pascal, для вспомогательных функций тегов преобразуются в кебаб-стиль. Вспомогательная функция тегов для вызова компонента представления использует элемент <vc></vc>. Компонент представления задан следующим образом:

<vc:[view-component-name]
  parameter1="parameter1 value"
  parameter2="parameter2 value">
</vc:[view-component-name]>

Чтобы использовать компонент представления в качестве вспомогательного приложения тегов, зарегистрируйте с помощью директивы @addTagHelperсборку, содержащую этот компонент представления. Если компонент представления находится в сборке MyWebApp, добавьте следующую директиву в _ViewImports.cshtml файл:

@addTagHelper *, MyWebApp

Зарегистрировать компонент представления в качестве вспомогательной функции тегов можно в любом файле, который ссылается на этот компонент представления. Раздел Управление областью вспомогательной функции тегов содержит дополнительные сведения о том, как зарегистрировать вспомогательные функции тегов.

Метод InvokeAsync, используемый в этом учебнике:

@await Component.InvokeAsync("PriorityList", new { maxPriority = 4, isDone = true })

В разметке помощника тегов:

<vc:priority-list max-priority="2" is-done="false">
</vc:priority-list>

В предыдущем примере компонент представления PriorityList становится priority-list. Параметры передаются в компонент представления как атрибуты в кебаб-стиле.

Вызов компонента представления непосредственно из контроллера

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

В этом примере компонент представления вызывается непосредственно из контроллера:

public IActionResult IndexVC()
{
    return ViewComponent("PriorityList", new { maxPriority = 3, isDone = false });
}

Пошаговое руководство. Создание простого компонента представления

Скачайте начальный код и выполните его сборку и тестирование. Это простой проект с контроллером ToDo, который отображает список задач.

Список дел

Добавление класса ViewComponent

Создайте папку ViewComponents и добавьте следующий класс PriorityListViewComponent:

using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using ViewComponentSample.Models;

namespace ViewComponentSample.ViewComponents
{
    public class PriorityListViewComponent : ViewComponent
    {
        private readonly ToDoContext db;

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

        public async Task<IViewComponentResult> InvokeAsync(
        int maxPriority, bool isDone)
        {
            var items = await GetItemsAsync(maxPriority, isDone);
            return View(items);
        }
        private Task<List<TodoItem>> GetItemsAsync(int maxPriority, bool isDone)
        {
            return db.ToDo.Where(x => x.IsDone == isDone &&
                                 x.Priority <= maxPriority).ToListAsync();
        }
    }
}

Примечания к коду:

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

  • Так как имя класса PriorityListViewComponent заканчивается суффиксом ViewComponent, среда выполнения использует строку PriorityList при ссылке на компонент класса из представления.

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

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

    • Название PriorityList, используемое при поиске представлений, связанных с компонентом
    • Строка PriorityList при ссылке на компонент класса из представления.
  • Компонент использует внедрение зависимостей для предоставления контекста данных.

  • InvokeAsync предоставляет метод, который возможно вызывать из представления и который может принять произвольное число аргументов.

  • Метод InvokeAsync возвращает набор элементов ToDo, удовлетворяющих параметрам isDone и maxPriority.

Создайте компонент представления Razor

  • Создайте папку Views/Shared/Components. Эта папка должна быть названа Components.

  • Создайте папку Views/Shared/Components/PriorityList. Имя папки должно соответствовать имени класса представления компонентов или имени класса без суффикса (если мы следуем соглашению и используем суффикс ViewComponent в названии класса). Если вы использовали атрибут ViewComponent, имя класса должно соответствовать его обозначению.

  • Views/Shared/Components/PriorityList/Default.cshtml Razor Создайте представление:

    @model IEnumerable<ViewComponentSample.Models.TodoItem>
    
    <h3>Priority Items</h3>
    <ul>
        @foreach (var todo in Model)
        {
            <li>@todo.Name</li>
        }
    </ul>
    

    Представление Razor принимает список TodoItem и отображает их. Если метод InvokeAsync компонента представления не передает имя представления (как в нашем примере), по соглашению используется имя Default. Далее в этом учебнике я покажу, как передать имя представления. Чтобы переопределить стиль по умолчанию для определенного контроллера, добавьте представление в папку соответствующего контроллера (например, Views/ToDo/Components/PriorityList/Default.cshtml).

    Если компонент представления зависит от контроллера, его можно добавить в папку для конкретного контроллера (Views/ToDo/Components/PriorityList/Default.cshtml).

  • Добавьте div, содержащий вызов компонента списка приоритетов, в нижнюю часть файла Views/ToDo/index.cshtml.

    </table>
    <div>
        @await Component.InvokeAsync("PriorityList", new { maxPriority = 2, isDone = false })
    </div>
    

Разметка @await Component.InvokeAsync показывает синтаксис для вызова компонентов представления. Первым аргументом является имя компонента, который требуется вызвать. Последующие параметры передаются в компонент. InvokeAsync может занять произвольное число аргументов.

Тестирование приложения. Следующий рисунок показывает список дел и элементы с приоритетом:

список дел и элементы с приоритетом

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

public IActionResult IndexVC()
{
    return ViewComponent("PriorityList", new { maxPriority = 3, isDone = false });
}

элементы с приоритетом из действия IndexVC

Указание имени представления

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

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/Shared/Components/PriorityList/Default.cshtml в представление с именем Views/Shared/Components/PriorityList/PVC.cshtml. Добавьте заголовок, чтобы указать, что используется представление PVC.

@model IEnumerable<ViewComponentSample.Models.TodoItem>

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

Обновление Views/ToDo/Index.cshtml:

@await Component.InvokeAsync("PriorityList", new { maxPriority = 4, isDone = true })

Запустите приложение и проверьте отображение PVC.

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

Если представление PVC не отображается, убедитесь, что вы вызываете компонент представления с приоритетом 4 или выше.

Изучите путь к представлению

  • Задайте для параметра приоритета значение 3 или меньше, чтобы представление с приоритетом не возвращалось.

  • Временно переименуйте Views/ToDo/Components/PriorityList/Default.cshtml на 1Default.cshtml.

  • Проверьте приложение, при этом происходит следующая ошибка:

    An unhandled exception occurred while processing the request.
    InvalidOperationException: The view 'Components/PriorityList/Default' wasn't found. The following locations were searched:
    /Views/ToDo/Components/PriorityList/Default.cshtml
    /Views/Shared/Components/PriorityList/Default.cshtml
    EnsureSuccessful
    
  • Скопируйте Views/ToDo/Components/PriorityList/1Default.cshtml в Views/Shared/Components/PriorityList/Default.cshtml.

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

  • Протестируйте представление компонента Shared.

Вывод задач с использованием компонента Shared

Как избежать жестко запрограммированных строк

Чтобы обеспечить безопасность во время компиляции, можно заменить жестко заданное имя компонента представления на имя класса. Создайте компонент представления без суффикса "ViewComponent":

using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using ViewComponentSample.Models;

namespace ViewComponentSample.ViewComponents
{
    public class PriorityList : ViewComponent
    {
        private readonly ToDoContext db;

        public PriorityList(ToDoContext context)
        {
            db = context;
        }

        public async Task<IViewComponentResult> InvokeAsync(
        int maxPriority, bool isDone)
        {
            var items = await GetItemsAsync(maxPriority, isDone);
            return View(items);
        }
        private Task<List<TodoItem>> GetItemsAsync(int maxPriority, bool isDone)
        {
            return db.ToDo.Where(x => x.IsDone == isDone &&
                                 x.Priority <= maxPriority).ToListAsync();
        }
    }
}

Добавьте инструкцию using в ваш файл представления Razor, а затем используйте оператор nameof.

@using ViewComponentSample.Models
@using ViewComponentSample.ViewComponents
@model IEnumerable<TodoItem>

    <h2>ToDo nameof</h2>
    <!-- Markup removed for brevity.  -->

    <div>

        @*
            Note: 
            To use the below line, you need to #define no_suffix in ViewComponents/PriorityList.cs or it won't compile.
            By doing so it will cause a problem to index as there will be multiple viewcomponents 
            with the same name after the compiler removes the suffix "ViewComponent"
        *@

        @*@await Component.InvokeAsync(nameof(PriorityList), new { maxPriority = 4, isDone = true })*@
    </div>

Вы можете использовать перегрузку метода Component.InvokeAsync, который принимает тип CLR. Не забудьте в этом случае использовать оператор typeof.

@using ViewComponentSample.Models
@using ViewComponentSample.ViewComponents
@model IEnumerable<TodoItem>

<h2>ToDo typeof</h2>
<!-- Markup removed for brevity.  -->

<div>
    @await Component.InvokeAsync(typeof(PriorityListViewComponent), new { maxPriority = 4, isDone = true })
</div>

Выполнение синхронных задач

Платформа обрабатывает вызов синхронного метода Invoke, если не требуется асинхронное выполнение работы. Следующий метод создает синхронный компонент представления Invoke:

public class PriorityList : ViewComponent
{
    public IViewComponentResult Invoke(int maxPriority, bool isDone)
    {
        var items = new List<string> { $"maxPriority: {maxPriority}", $"isDone: {isDone}" };
        return View(items);
    }
}

Файл компонента Razor представления содержит строки, передаваемые методу Invoke (Views/Home/Components/PriorityList/Default.cshtml):

@model List<string>

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

Компонент представления вызывается в Razor файле (например, Views/Home/Index.cshtmlс помощью одного из следующих подходов:

Чтобы использовать подход IViewComponentHelper, вызовите Component.InvokeAsync:

@await Component.InvokeAsync(nameof(PriorityList), new { maxPriority = 4, isDone = true })

Чтобы использовать вспомогательное приложение тэгов зарегистрируйте с помощью директивы @addTagHelperсборку, содержащую этот компонент представления (он находится в сборке с именем MyWebApp):

@addTagHelper *, MyWebApp

Используйте вспомогательный элемент тега компонента представления в файле разметки Razor :

<vc:priority-list max-priority="999" is-done="false">
</vc:priority-list>

Сигнатура PriorityList.Invoke метода синхронна, но Razor находит и вызывает метод в Component.InvokeAsync файле разметки.

Все параметры компонентов представления обязательны

Каждый параметр в компоненте представления является обязательным атрибутом. Также см. эту проблему в GitHub. Если какой-либо параметр отсутствует:

  • Сигнатура метода InvokeAsync не совпадет, поэтому метод не будет выполняться.
  • Компонент ViewComponent не отобразит никакой разметки.
  • Ошибки не будут возникать.

Дополнительные ресурсы