Просмотр компонентов в 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 " в приложения ASP.NET Core.

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

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

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

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

  • Производный от 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/{Имя компонента представления}/{Имя представления}
  • /Area/{Имя области}/Views/Shared/Components/{View Component Name}/{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 .

List of ToDos

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

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.cshtmlRazor Создайте представление:

    @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 может занять произвольное число аргументов.

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

todo list and priority items

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

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

priority items from IndexVC action

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

Сложному представлению компонента в некоторых условиях может потребоваться указать нестандартное представление. Ниже показано, как указать представление "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.

Priority View Component

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

Проверка пути к представлению

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

  • Временно переименуйте в Views/ToDo/Components/PriorityList/Default.cshtml1Default.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.

ToDo output with Shared component view

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

Для обеспечения безопасности во времени компиляции замените жестко закодированное имя компонента представления именем класса. Обновите файл 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 CoreRazor.

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

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

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

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

  • Производный от 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/{Имя компонента представления}/{Имя представления}
  • /Area/{Имя области}/Views/Shared/Components/{View Component Name}/{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 .

List of ToDos

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

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.cshtmlRazor Создайте представление:

    @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 может занять произвольное число аргументов.

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

todo list and priority items

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

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

priority items from IndexVC action

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

Сложному представлению компонента в некоторых условиях может потребоваться указать нестандартное представление. Ниже показано, как указать представление "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.

Priority View Component

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

Проверка пути к представлению

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

  • Временно переименуйте в Views/ToDo/Components/PriorityList/Default.cshtml1Default.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.

ToDo output with Shared component view

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

Для обеспечения безопасности во времени компиляции замените жестко закодированное имя компонента представления именем класса. Обновите файл 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 CoreRazor.

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

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

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

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

  • Наследование от 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/{Имя компонента представления}/{Имя представления}
  • /Area/{Имя области}/Views/Shared/Components/{View Component Name}/{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, который отображает список элементов дел.

List of ToDos

Добавление класса 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.cshtmlRazor Создайте представление:

    @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 может занять произвольное число аргументов.

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

todo list and priority items

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

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

priority items from IndexVC action

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

Сложному представлению компонента в некоторых условиях может потребоваться указать нестандартное представление. Ниже показано, как указать представление "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.

Priority View Component

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

Проверка пути к представлению

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

  • Временно переименуйте в Views/ToDo/Components/PriorityList/Default.cshtml1Default.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, чтобы указать, что это представление из папки Shared.

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

ToDo output with Shared component view

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

Чтобы обеспечить безопасность во время компиляции, можно заменить жестко заданное имя компонента представления на имя класса. Создайте компонент представления без суффикса "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();
        }
    }
}

Добавьте инструкцию usingnameof в Razor файл представления и используйте оператор:

@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 не отобразит никакой разметки.
  • Не будут выдаваться ошибки.

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