Partilhar via


Ver componentes no ASP.NET Core

Por Rick Anderson

Ver componentes

Os componentes de visualização são semelhantes às vistas parciais, mas são muito mais poderosos. Os componentes de exibição não usam vinculação de modelo, eles dependem dos dados passados ao chamar o componente de exibição. Este artigo foi escrito usando controladores e modos de exibição, mas os componentes de exibição funcionam com Razor o Pages.

Um componente de visualização:

  • Renderiza uma parte em vez de uma resposta inteira.
  • Inclui os mesmos benefícios de separação de responsabilidades e capacidade de teste encontrados entre um controlador e uma visualização.
  • Pode ter parâmetros e lógica de negócio.
  • Normalmente é invocado a partir de uma página de layout.

Os componentes de exibição destinam-se a serem utilizados em qualquer lugar onde a lógica de renderização que possa ser reutilizada seja demasiado complexa para uma exibição parcial, como:

  • Menus de navegação dinâmica
  • Nuvem de tags, onde consulta o banco de dados
  • Painel Iniciar sessão
  • Carrinho de compras
  • Artigos publicados recentemente
  • Conteúdo da barra lateral em um blog
  • Um painel de autenticação que seria renderizado em cada página e mostraria os links para terminar sessão ou iniciar sessão, dependendo do estado de autenticação do utilizador.

Um componente de exibição consiste em duas partes:

  • A classe, normalmente derivada de ViewComponent
  • O resultado que ele retorna, normalmente uma visualização.

Como os controladores, um componente de exibição pode ser um POCO, mas a maioria dos desenvolvedores aproveita os métodos e propriedades disponíveis derivando do ViewComponent.

Ao considerar se os componentes de exibição atendem às especificações de um aplicativo, considere usar componentes Razor em vez disso. Razor componentes também combinam marcação com código C# para produzir unidades de interface do usuário reutilizáveis. Razor componentes são projetados para aumentar a produtividade dos desenvolvedores ao oferecer lógica e composição da interface de utilizador no lado do cliente. Para obter mais informações, consulte componentes do ASP.NET Core Razor. Para obter informações sobre como incorporar Razor componentes em um aplicativo MVC ou Razor Pages, consulte Integrar componentes ASP.NET Core Razor com MVC ou Razor Pages.

Criar um componente de exibição

Esta seção contém os requisitos de alto nível para criar um componente de exibição. Mais adiante no artigo, examinaremos cada etapa em detalhes e criaremos um componente de exibição.

A classe do componente de exibição

Uma classe de componente de exibição pode ser criada por qualquer um dos seguintes:

Como os controladores, os componentes de visualização devem ser classes públicas, não aninhadas e não abstratas. O nome do componente de exibição é o nome da classe com o sufixo ViewComponent removido. Ele também pode ser explicitamente especificado usando a Name propriedade.

Uma classe de componente de exibição:

Para evitar que uma classe que tenha um sufixo que não diferencia maiúsculas de minúsculas ViewComponent seja tratada como um componente de exibição, decore a classe com o [NonViewComponent] atributo:

using Microsoft.AspNetCore.Mvc;

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

Exibir métodos de componentes

Um componente de exibição define sua lógica em:

  • InvokeAsync método que retorna Task<IViewComponentResult>.
  • Invoke método síncrono que retorna um IViewComponentResult.

Os parâmetros vêm diretamente da invocação do componente de exibição, não da vinculação do modelo. Um componente de exibição nunca lida diretamente com uma solicitação. Normalmente, uma vista inicializa um modelo e passa-o para outra vista chamando o método View. Em resumo, consulte os métodos do componente:

  • Defina um InvokeAsync método que retorna um Task<IViewComponentResult> ou um método síncrono Invoke que retorna um IViewComponentResult.
  • Normalmente, inicializa um modelo e o passa para um modo de exibição chamando o método ViewComponent.View .
  • Os parâmetros vêm do método de chamada, não HTTP. Não há vinculação de modelo.
  • Não são acessíveis diretamente como um endpoint HTTP. Eles geralmente são invocados em um modo de exibição. Um componente de exibição nunca lida com uma solicitação.
  • Estão sobrecarregados na assinatura em vez de quaisquer detalhes da solicitação HTTP atual.

Ver caminho de pesquisa

O runtime procura a vista nos seguintes caminhos:

  • /Views/{Nome do Controlador}/Componentes/{Nome do Componente de Vista}/{Nome da Vista}
  • /views/shareed/components/{view component name}/{view name}
  • /Pages/Shared/Components/{Nome do Componente de Visualização}/{Nome da Visualização}
  • /areas/{Area Name}/views/shared/components/{view component name}/{view name}

O caminho de pesquisa aplica-se a projetos que usam controladores + visualizações e Razor Páginas.

O nome de exibição padrão para um componente de exibição é Default, o que significa que os arquivos de exibição normalmente serão nomeados Default.cshtml. Um nome de exibição diferente pode ser especificado ao criar o resultado do componente de exibição ou ao chamar o View método.

Recomendamos nomear o arquivo Default.cshtml de exibição e usar o caminho Views/Shared/Components/{View Component Name}/{View Name} . O componente de visualização PriorityList usado neste exemplo utiliza Views/Shared/Components/PriorityList/Default.cshtml para a renderização do componente de visualização.

Personalizar o caminho de pesquisa da exibição

Para personalizar o caminho de pesquisa da exibição, modifique a coleção Razor de ViewLocationFormats. Por exemplo, para pesquisar visões dentro do caminho /Components/{View Component Name}/{View Name}, adicione um novo item à coleção:

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.

No código anterior, o espaço reservado {0} representa o caminho Components/{View Component Name}/{View Name}.

Invocar um componente de exibição

Para usar o componente de exibição, chame o seguinte dentro de uma visualização:

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

Os parâmetros são passados para o InvokeAsync método. O componente de exibição PriorityList desenvolvido no artigo é invocado no arquivo de exibição Views/ToDo/Index.cshtml. No código a seguir, o InvokeAsync método é chamado com dois parâmetros:

</table>

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

Invoque um componente de exibição como um Auxiliar de Tag

Um componente View pode ser invocado como um auxiliar de tag:

<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>

Os parâmetros de classe e método em PascalCase para Tag Helpers são traduzidos para o seu caso de kebab. O Auxiliar de Tag para invocar um componente de exibição usa o <vc></vc> elemento . O componente de exibição é especificado da seguinte maneira:

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

Para usar um componente de visualização como um auxiliar de tag, registe o assembly que contém o componente de visualização usando a diretiva @addTagHelper. Se o componente de exibição estiver em um assembly chamado MyWebApp, adicione a seguinte diretiva ao _ViewImports.cshtml arquivo:

@addTagHelper *, MyWebApp

Um componente de visualização pode ser registado como um Tag Helper para qualquer ficheiro que faça referência ao componente de visualização. Consulte Gerenciando o escopo do auxiliar de tag para obter mais informações sobre como registrar auxiliares de tag.

O InvokeAsync método usado neste tutorial:

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

Na marcação anterior, o PriorityList componente de exibição torna-se priority-list. Os parâmetros para o componente de visualização são passados como atributos em kebab case.

Invoque um componente de exibição diretamente de um controlador

Os componentes de exibição normalmente são invocados a partir de uma vista, mas podem ser invocados diretamente de um método do controlador. Embora os componentes de visualização não definam pontos de extremidade como os controladores, uma ação do controlador que retorne o conteúdo de um ViewComponentResult pode ser implementada.

No exemplo a seguir, o componente de exibição é chamado diretamente do controlador:

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

Criar um componente de exibição básica

Baixe, construa e teste o código inicial. É um projeto básico com um ToDo controlador que exibe uma lista de itens ToDo .

Lista de ToDos

Atualize o controlador para passar o status de prioridade e conclusão

Atualize o método Index para usar os parâmetros de prioridade e status de conclusão:

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);
    }

Adicionar uma classe ViewComponent

Adicione uma classe ViewComponent a 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();
    }
}

Notas sobre o código:

  • As classes de componente de exibição podem estar contidas em qualquer pasta do projeto.

  • Como o nome da classe PriorityListViewComponent termina com o sufixo ViewComponent, o tempo de execução usa a cadeia de caracteres PriorityList ao fazer referência ao componente de classe de uma exibição.

  • O [ViewComponent] atributo pode alterar o nome usado para fazer referência a um componente de exibição. Por exemplo, a classe poderia ter sido nomeada XYZ com o seguinte [ViewComponent] atributo:

    [ViewComponent(Name = "PriorityList")]
       public class XYZ : ViewComponent
    
  • O [ViewComponent] atributo no código anterior informa ao seletor de componente de exibição para usar:

    • O nome PriorityList ao procurar os modos de exibição associados ao componente
    • A cadeia de caracteres "PriorityList" ao fazer referência ao componente de classe a partir de uma exibição.
  • O componente usa a injeção de dependência para disponibilizar o contexto de dados.

  • InvokeAsync expõe um método que pode ser chamado a partir de uma vista e pode aceitar um número arbitrário de argumentos.

  • O InvokeAsync método retorna o conjunto de ToDo itens que satisfazem os isDone parâmetros e maxPriority .

Criar o componente de vista Razor

  • Crie a pasta Views/Shared/Components . Esta pasta deve ser denominada Componentes.

  • Crie a pasta Views/Shared/Components/PriorityList. Esse nome de pasta deve corresponder ao nome da classe do componente de exibição ou ao nome da classe menos o sufixo. Se o ViewComponent atributo for usado, o nome da classe precisará corresponder à designação do atributo.

  • Crie uma Views/Shared/Components/PriorityList/Default.cshtmlRazor vista:

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

    O Razor recebe uma lista de TodoItem e as exibe. Se o método do componente InvokeAsync não passar o nome da vista, Default será usado como o nome da vista por convenção. Para substituir o estilo padrão de um controlador específico, adicione uma vista à pasta de vistas específica do controlador (por exemplo Views/ToDo/Components/PriorityList/Default.cshtml).

    Se o componente de exibição for específico do controlador, ele poderá ser adicionado à pasta específica do controlador. Por exemplo, Views/ToDo/Components/PriorityList/Default.cshtml é específico do controlador.

  • Adicione um div que contenha uma chamada para o componente da lista de prioridades na parte inferior do arquivo 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>
    

A marcação @await Component.InvokeAsync mostra a sintaxe para chamar componentes de exibição. O primeiro argumento é o nome do componente que queremos invocar ou chamar. Os parâmetros subsequentes são passados para o componente. InvokeAsync pode aceitar um número arbitrário de argumentos.

Teste o aplicativo. A imagem a seguir mostra a lista de ToDo e os itens prioritários:

Lista de tarefas e itens prioritários

O componente de visualização pode ser chamado diretamente do controlador:

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

itens prioritários da ação IndexVC

Especificar um nome de componente de exibição

Um componente de exibição complexo pode precisar especificar um modo de exibição não padrão em algumas condições. O código a seguir mostra como especificar a exibição "PVC" do método InvokeAsync. Atualize o método InvokeAsync na classe 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);
}

Copie o Views/Shared/Components/PriorityList/Default.cshtml arquivo para um modo de exibição chamado Views/Shared/Components/PriorityList/PVC.cshtml. Adicione um título para indicar que a visualização PVC está a ser utilizada.

@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>

Execute o aplicativo e verifique a visualização PVC.

Componente de visualização prioritária

Se a visualização PVC não for renderizada, verifique se o componente de exibição com prioridade de 4 ou superior é chamado.

Examinar o caminho de exibição

  • Altere o parâmetro priority para três ou menos para que a exibição de prioridade não seja retornada.

  • Renomeie temporariamente o Views/ToDo/Components/PriorityList/Default.cshtml para 1Default.cshtml.

  • Teste o aplicativo, ocorre o seguinte erro:

    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
    
  • Copie Views/ToDo/Components/PriorityList/1Default.cshtml para Views/Shared/Components/PriorityList/Default.cshtml.

  • Adicione alguma marcação ao componente de vista ToDo Partilhada para indicar que a vista é proveniente da pasta Partilhada.

  • Teste a vista Compartilhada do componente.

Saída de ToDo com visualização de componente compartilhado

Evite cadeias de caracteres codificadas

Para segurança no tempo de compilação, substitua o nome do componente de exibição codificado pelo nome da classe. Atualize o arquivo PriorityListViewComponent.cs para não usar o sufixo "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();
    }
}

O arquivo de visualização:

</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>

Uma sobrecarga do método Component.InvokeAsync que aceita um tipo CLR utiliza o operador 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>

Executar trabalho síncrono

A estrutura lida com a invocação de um método síncrono Invoke se o trabalho assíncrono não for necessário. O método a seguir cria um componente de exibição síncrona 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);
        }
    }
}

O ficheiro do componente de visualização 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>

O componente de exibição é invocado em um Razor arquivo (por exemplo, Views/Home/Index.cshtml) usando uma das seguintes abordagens:

Para usar a IViewComponentHelper abordagem, ligue para Component.InvokeAsync:

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

Para usar o Auxiliar de tag, registre o assembly que contém o componente View usando a @addTagHelper diretiva (o componente view está em um assembly chamado MyWebApp):

@addTagHelper *, MyWebApp

Use o componente de vista Tag Helper no arquivo de marcação Razor.

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

A assinatura do método PriorityList.Invoke é síncrona, mas Razor localiza e chama o método com Component.InvokeAsync no ficheiro de marcação.

Recursos adicionais

Ver componentes

Os componentes de visualização são semelhantes às vistas parciais, mas são muito mais poderosos. Os componentes de exibição não usam vinculação de modelo, eles dependem dos dados passados ao chamar o componente de exibição. Este artigo foi escrito usando controladores e modos de exibição, mas os componentes de exibição funcionam com Razor o Pages.

Um componente de visualização:

  • Renderiza uma parte em vez de uma resposta inteira.
  • Inclui os mesmos benefícios de separação de responsabilidades e capacidade de teste encontrados entre um controlador e uma visualização.
  • Pode ter parâmetros e lógica de negócio.
  • Normalmente é invocado a partir de uma página de layout.

Os componentes de exibição destinam-se a serem utilizados em qualquer lugar onde a lógica de renderização que possa ser reutilizada seja demasiado complexa para uma exibição parcial, como:

  • Menus de navegação dinâmica
  • Nuvem de tags, onde consulta o banco de dados
  • Painel Iniciar sessão
  • Carrinho de compras
  • Artigos publicados recentemente
  • Conteúdo da barra lateral em um blog
  • Um painel de autenticação que seria renderizado em cada página e mostraria os links para terminar sessão ou iniciar sessão, dependendo do estado de autenticação do utilizador.

Um componente de exibição consiste em duas partes:

  • A classe, normalmente derivada de ViewComponent
  • O resultado que ele retorna, normalmente uma visualização.

Como os controladores, um componente de exibição pode ser um POCO, mas a maioria dos desenvolvedores aproveita os métodos e propriedades disponíveis derivando do ViewComponent.

Ao considerar se os componentes de exibição atendem às especificações de um aplicativo, considere usar componentes Razor em vez disso. Razor componentes também combinam marcação com código C# para produzir unidades de interface do usuário reutilizáveis. Razor componentes são projetados para aumentar a produtividade dos desenvolvedores ao oferecer lógica e composição da interface de utilizador no lado do cliente. Para obter mais informações, consulte componentes do ASP.NET Core Razor. Para obter informações sobre como incorporar Razor componentes em um aplicativo MVC ou Razor Pages, consulte Integrar componentes ASP.NET Core Razor com MVC ou Razor Pages.

Criar um componente de exibição

Esta seção contém os requisitos de alto nível para criar um componente de exibição. Mais adiante no artigo, examinaremos cada etapa em detalhes e criaremos um componente de exibição.

A classe do componente de exibição

Uma classe de componente de exibição pode ser criada por qualquer um dos seguintes:

Como os controladores, os componentes de visualização devem ser classes públicas, não aninhadas e não abstratas. O nome do componente de exibição é o nome da classe com o sufixo ViewComponent removido. Ele também pode ser explicitamente especificado usando a Name propriedade.

Uma classe de componente de exibição:

Para evitar que uma classe que tenha um sufixo que não diferencia maiúsculas de minúsculas ViewComponent seja tratada como um componente de exibição, decore a classe com o [NonViewComponent] atributo:

using Microsoft.AspNetCore.Mvc;

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

Exibir métodos de componentes

Um componente de exibição define sua lógica em:

  • InvokeAsync método que retorna Task<IViewComponentResult>.
  • Invoke método síncrono que retorna um IViewComponentResult.

Os parâmetros vêm diretamente da invocação do componente de exibição, não da vinculação do modelo. Um componente de exibição nunca lida diretamente com uma solicitação. Normalmente, uma vista inicializa um modelo e passa-o para outra vista chamando o método View. Em resumo, consulte os métodos do componente:

  • Defina um InvokeAsync método que retorna um Task<IViewComponentResult> ou um método síncrono Invoke que retorna um IViewComponentResult.
  • Normalmente, inicializa um modelo e o passa para um modo de exibição chamando o método ViewComponent.View .
  • Os parâmetros vêm do método de chamada, não HTTP. Não há vinculação de modelo.
  • Não são acessíveis diretamente como um endpoint HTTP. Eles geralmente são invocados em um modo de exibição. Um componente de exibição nunca lida com uma solicitação.
  • Estão sobrecarregados na assinatura em vez de quaisquer detalhes da solicitação HTTP atual.

Ver caminho de pesquisa

O runtime procura a vista nos seguintes caminhos:

  • /Views/{Nome do Controlador}/Componentes/{Nome do Componente de Vista}/{Nome da Vista}
  • /views/shareed/components/{view component name}/{view name}
  • /Pages/Shared/Components/{Nome do Componente de Visualização}/{Nome da Visualização}
  • /areas/{Area Name}/views/shared/components/{view component name}/{view name}

O caminho de pesquisa aplica-se a projetos que usam controladores + visualizações e Razor Páginas.

O nome de exibição padrão para um componente de exibição é Default, o que significa que os arquivos de exibição normalmente serão nomeados Default.cshtml. Um nome de exibição diferente pode ser especificado ao criar o resultado do componente de exibição ou ao chamar o View método.

Recomendamos nomear o arquivo Default.cshtml de exibição e usar o caminho Views/Shared/Components/{View Component Name}/{View Name} . O componente de visualização PriorityList usado neste exemplo utiliza Views/Shared/Components/PriorityList/Default.cshtml para a renderização do componente de visualização.

Personalizar o caminho de pesquisa da exibição

Para personalizar o caminho de pesquisa da exibição, modifique a coleção Razor de ViewLocationFormats. Por exemplo, para pesquisar visões dentro do caminho /Components/{View Component Name}/{View Name}, adicione um novo item à coleção:

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.

No código anterior, o espaço reservado {0} representa o caminho Components/{View Component Name}/{View Name}.

Invocar um componente de exibição

Para usar o componente de exibição, chame o seguinte dentro de uma visualização:

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

Os parâmetros são passados para o InvokeAsync método. O componente de exibição PriorityList desenvolvido no artigo é invocado no arquivo de exibição Views/ToDo/Index.cshtml. No código a seguir, o InvokeAsync método é chamado com dois parâmetros:

</table>

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

Invoque um componente de exibição como um Auxiliar de Tag

Um componente View pode ser invocado como um auxiliar de tag:

<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>

Os parâmetros de classe e método em PascalCase para Tag Helpers são traduzidos para o seu caso de kebab. O Auxiliar de Tag para invocar um componente de exibição usa o <vc></vc> elemento . O componente de exibição é especificado da seguinte maneira:

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

Para usar um componente de visualização como um auxiliar de tag, registe o assembly que contém o componente de visualização usando a diretiva @addTagHelper. Se o componente de exibição estiver em um assembly chamado MyWebApp, adicione a seguinte diretiva ao _ViewImports.cshtml arquivo:

@addTagHelper *, MyWebApp

Um componente de visualização pode ser registado como um Tag Helper para qualquer ficheiro que faça referência ao componente de visualização. Consulte Gerenciando o escopo do auxiliar de tag para obter mais informações sobre como registrar auxiliares de tag.

O InvokeAsync método usado neste tutorial:

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

Na marcação anterior, o PriorityList componente de exibição torna-se priority-list. Os parâmetros para o componente de visualização são passados como atributos em kebab case.

Invoque um componente de exibição diretamente de um controlador

Os componentes de exibição normalmente são invocados a partir de uma vista, mas podem ser invocados diretamente de um método do controlador. Embora os componentes de visualização não definam pontos de extremidade como os controladores, uma ação do controlador que retorne o conteúdo de um ViewComponentResult pode ser implementada.

No exemplo a seguir, o componente de exibição é chamado diretamente do controlador:

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

Criar um componente de exibição básica

Baixe, construa e teste o código inicial. É um projeto básico com um ToDo controlador que exibe uma lista de itens ToDo .

Lista de ToDos

Atualize o controlador para passar o status de prioridade e conclusão

Atualize o método Index para usar os parâmetros de prioridade e status de conclusão:

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);
    }

Adicionar uma classe ViewComponent

Adicione uma classe ViewComponent a 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();
    }
}

Notas sobre o código:

  • As classes de componente de exibição podem estar contidas em qualquer pasta do projeto.

  • Como o nome da classe PriorityListViewComponent termina com o sufixo ViewComponent, o tempo de execução usa a cadeia de caracteres PriorityList ao fazer referência ao componente de classe de uma exibição.

  • O [ViewComponent] atributo pode alterar o nome usado para fazer referência a um componente de exibição. Por exemplo, a classe poderia ter sido nomeada XYZ com o seguinte [ViewComponent] atributo:

    [ViewComponent(Name = "PriorityList")]
       public class XYZ : ViewComponent
    
  • O [ViewComponent] atributo no código anterior informa ao seletor de componente de exibição para usar:

    • O nome PriorityList ao procurar os modos de exibição associados ao componente
    • A cadeia de caracteres "PriorityList" ao fazer referência ao componente de classe a partir de uma exibição.
  • O componente usa a injeção de dependência para disponibilizar o contexto de dados.

  • InvokeAsync expõe um método que pode ser chamado a partir de uma vista e pode aceitar um número arbitrário de argumentos.

  • O InvokeAsync método retorna o conjunto de ToDo itens que satisfazem os isDone parâmetros e maxPriority .

Criar o componente de vista Razor

  • Crie a pasta Views/Shared/Components . Esta pasta deve ser denominada Componentes.

  • Crie a pasta Views/Shared/Components/PriorityList. Esse nome de pasta deve corresponder ao nome da classe do componente de exibição ou ao nome da classe menos o sufixo. Se o ViewComponent atributo for usado, o nome da classe precisará corresponder à designação do atributo.

  • Crie uma Views/Shared/Components/PriorityList/Default.cshtmlRazor vista:

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

    O Razor recebe uma lista de TodoItem e as exibe. Se o método do componente InvokeAsync não passar o nome da vista, Default será usado como o nome da vista por convenção. Para substituir o estilo padrão de um controlador específico, adicione uma vista à pasta de vistas específica do controlador (por exemplo Views/ToDo/Components/PriorityList/Default.cshtml).

    Se o componente de exibição for específico do controlador, ele poderá ser adicionado à pasta específica do controlador. Por exemplo, Views/ToDo/Components/PriorityList/Default.cshtml é específico do controlador.

  • Adicione um div que contenha uma chamada para o componente da lista de prioridades na parte inferior do arquivo 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>
    

A marcação @await Component.InvokeAsync mostra a sintaxe para chamar componentes de exibição. O primeiro argumento é o nome do componente que queremos invocar ou chamar. Os parâmetros subsequentes são passados para o componente. InvokeAsync pode aceitar um número arbitrário de argumentos.

Teste o aplicativo. A imagem a seguir mostra a lista de ToDo e os itens prioritários:

Lista de tarefas e itens prioritários

O componente de visualização pode ser chamado diretamente do controlador:

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

itens prioritários da ação IndexVC

Especificar um nome de componente de exibição

Um componente de exibição complexo pode precisar especificar um modo de exibição não padrão em algumas condições. O código a seguir mostra como especificar a exibição "PVC" do método InvokeAsync. Atualize o método InvokeAsync na classe 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);
}

Copie o Views/Shared/Components/PriorityList/Default.cshtml arquivo para um modo de exibição chamado Views/Shared/Components/PriorityList/PVC.cshtml. Adicione um título para indicar que a visualização PVC está a ser utilizada.

@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>

Execute o aplicativo e verifique a visualização PVC.

Componente de visualização prioritária

Se a visualização PVC não for renderizada, verifique se o componente de exibição com prioridade de 4 ou superior é chamado.

Examinar o caminho de exibição

  • Altere o parâmetro priority para três ou menos para que a exibição de prioridade não seja retornada.

  • Renomeie temporariamente o Views/ToDo/Components/PriorityList/Default.cshtml para 1Default.cshtml.

  • Teste o aplicativo, ocorre o seguinte erro:

    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
    
  • Copie Views/ToDo/Components/PriorityList/1Default.cshtml para Views/Shared/Components/PriorityList/Default.cshtml.

  • Adicione alguma marcação ao componente de vista ToDo Partilhada para indicar que a vista é proveniente da pasta Partilhada.

  • Teste a vista Compartilhada do componente.

Saída de ToDo com visualização de componente compartilhado

Evite cadeias de caracteres codificadas

Para segurança no tempo de compilação, substitua o nome do componente de exibição codificado pelo nome da classe. Atualize o arquivo PriorityListViewComponent.cs para não usar o sufixo "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();
    }
}

O arquivo de visualização:

</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>

Uma sobrecarga do método Component.InvokeAsync que aceita um tipo CLR utiliza o operador 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>

Executar trabalho síncrono

A estrutura lida com a invocação de um método síncrono Invoke se o trabalho assíncrono não for necessário. O método a seguir cria um componente de exibição síncrona 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);
        }
    }
}

O ficheiro do componente de visualização 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>

O componente de exibição é invocado em um Razor arquivo (por exemplo, Views/Home/Index.cshtml) usando uma das seguintes abordagens:

Para usar a IViewComponentHelper abordagem, ligue para Component.InvokeAsync:

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

Para usar o Auxiliar de tag, registre o assembly que contém o componente View usando a @addTagHelper diretiva (o componente view está em um assembly chamado MyWebApp):

@addTagHelper *, MyWebApp

Use o componente de vista Tag Helper no arquivo de marcação Razor.

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

A assinatura do método PriorityList.Invoke é síncrona, mas Razor localiza e chama o método com Component.InvokeAsync no ficheiro de marcação.

Recursos adicionais

Visualizar ou descarregar amostra de código (como descarregar)

Ver componentes

Os componentes de visualização são semelhantes às vistas parciais, mas são muito mais poderosos. Os componentes de exibição não usam vinculação de modelo e dependem apenas dos dados fornecidos ao chamá-la. Este artigo foi escrito usando controladores e modos de exibição, mas os componentes de exibição também funcionam com Razor o Pages.

Um componente de visualização:

  • Renderiza uma parte em vez de uma resposta inteira.
  • Inclui os mesmos benefícios de separação de responsabilidades e capacidade de teste encontrados entre um controlador e uma visualização.
  • Pode ter parâmetros e lógica de negócio.
  • Normalmente é invocado a partir de uma página de layout.

Os componentes de exibição destinam-se a qualquer lugar onde você tenha uma lógica de renderização reutilizável que seja muito complexa para uma exibição parcial, como:

  • Menus de navegação dinâmica
  • Nuvem de tags (onde consulta o banco de dados)
  • Painel de login
  • Carrinho de compras
  • Artigos publicados recentemente
  • Conteúdo da barra lateral em um blog típico
  • Um painel de login que seria renderizado em cada página e mostraria os links para logout ou login, dependendo do estado de login do usuário

Um componente de exibição consiste em duas partes: a classe (normalmente derivada de ViewComponent) e o resultado que retorna (normalmente uma exibição). Como os controladores, um componente de exibição pode ser um POCO, mas a maioria dos desenvolvedores aproveita os métodos e propriedades disponíveis derivando de ViewComponent.

Ao considerar se os componentes de exibição atendem às especificações de um aplicativo, considere usar componentes Razor em vez disso. Razor componentes também combinam marcação com código C# para produzir unidades de interface do usuário reutilizáveis. Razor componentes são projetados para aumentar a produtividade dos desenvolvedores ao oferecer lógica e composição da interface de utilizador no lado do cliente. Para obter mais informações, consulte componentes do ASP.NET Core Razor. Para obter informações sobre como incorporar Razor componentes em um aplicativo MVC ou Razor Pages, consulte Integrar componentes ASP.NET Core Razor com MVC ou Razor Pages.

Criando um componente de exibição

Esta seção contém os requisitos de alto nível para criar um componente de exibição. Mais adiante no artigo, examinaremos cada etapa em detalhes e criaremos um componente de exibição.

A classe do componente de exibição

Uma classe de componente de exibição pode ser criada por qualquer um dos seguintes:

  • Derivando de ViewComponent
  • Decorando uma classe com o [ViewComponent] atributo ou derivando de uma classe com o [ViewComponent] atributo
  • Criando uma classe onde o nome termina com o sufixo ViewComponent

Como os controladores, os componentes de visualização devem ser classes públicas, não aninhadas e não abstratas. O nome do componente de visualização é o nome da classe ao qual se removeu o sufixo "ViewComponent". Ele também pode ser explicitamente especificado usando a ViewComponentAttribute.Name propriedade.

Uma classe de componente de exibição:

  • Suporta totalmente a injeção de dependência do construtor
  • Não participa do ciclo de vida do controlador, o que significa que você não pode usar filtros em um componente de exibição

Para impedir que uma classe que tenha um sufixo ViewComponent que não diferencia maiúsculas de minúsculas seja tratada como um componente de exibição, decore a classe com o atributo [NonViewComponent] :

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

Exibir métodos de componentes

Um componente de exibição define sua lógica em um InvokeAsync método que retorna um Task<IViewComponentResult> ou em um método síncrono Invoke que retorna um IViewComponentResult. Os parâmetros vêm diretamente da invocação do componente de exibição, não da vinculação do modelo. Um componente de exibição nunca lida diretamente com uma solicitação. Normalmente, uma vista inicializa um modelo e passa-o para outra vista chamando o método View. Em resumo, consulte os métodos do componente:

  • Defina um InvokeAsync método que retorna um Task<IViewComponentResult> ou um método síncrono Invoke que retorna um IViewComponentResult.
  • Normalmente, inicializa um modelo e o passa para uma exibição chamando o ViewComponentView método.
  • Os parâmetros vêm do método de chamada, não HTTP. Não há vinculação de modelo.
  • Não são acessíveis diretamente como um endpoint HTTP. Eles são invocados a partir do seu código (geralmente em uma vista). Um componente de exibição nunca lida com uma solicitação.
  • Estão sobrecarregados na assinatura em vez de quaisquer detalhes da solicitação HTTP atual.

Ver caminho de pesquisa

O runtime procura a vista nos seguintes caminhos:

  • /Views/{Nome do Controlador}/Componentes/{Nome do Componente de Vista}/{Nome da Vista}
  • /views/shareed/components/{view component name}/{view name}
  • /Pages/Shared/Components/{Nome do Componente de Visualização}/{Nome da Visualização}
  • /areas/{Area Name}/views/shared/components/{view component name}/{view name}

O caminho de pesquisa aplica-se a projetos que usam controladores + visualizações e Razor Páginas.

O nome de exibição padrão para um componente de exibição é Padrão, o que significa que seu arquivo de exibição normalmente será nomeado Default.cshtml. Você pode especificar um nome de exibição diferente ao criar o resultado do componente de exibição ou ao chamar o View método.

Recomendamos que você nomeie o arquivo Default.cshtml de exibição e use o caminho Views/Shared/Components/{View Component Name}/{View Name} . O componente de visualização PriorityList usado neste exemplo utiliza Views/Shared/Components/PriorityList/Default.cshtml para a renderização do componente de visualização.

Personalizar o caminho de pesquisa da exibição

Para personalizar o caminho de pesquisa da exibição, modifique a coleção Razor de ViewLocationFormats. Por exemplo, para pesquisar visualizações dentro do caminho "/Components/{View Component Name}/{View Name}", adicione um novo item à coleção:

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

No código anterior, o espaço reservado "{0}" representa o caminho "Components/{View Component Name}/{View Name}".

Invocando um componente de exibição

Para usar o componente de exibição, chame o seguinte dentro de uma visualização:

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

Os parâmetros serão passados para o InvokeAsync método. O componente de exibição PriorityList desenvolvido no artigo é invocado no arquivo de exibição Views/ToDo/Index.cshtml. A seguir, o InvokeAsync método é chamado com dois parâmetros:

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

Invocando um componente de exibição como um Auxiliar de Tag

Para ASP.NET Core 1.1 e superior, você pode invocar um componente de exibição como um Auxiliar de tag:

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

Os parâmetros de classe e método em PascalCase para Tag Helpers são traduzidos para o seu caso de kebab. O Auxiliar de Tag para invocar um componente de exibição usa o <vc></vc> elemento . O componente de exibição é especificado da seguinte maneira:

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

Para usar um componente de visualização como um auxiliar de tag, registe o assembly que contém o componente de visualização usando a diretiva @addTagHelper. Se o componente de exibição estiver em um assembly chamado MyWebApp, adicione a seguinte diretiva ao _ViewImports.cshtml arquivo:

@addTagHelper *, MyWebApp

Você pode registrar um componente de exibição como um Auxiliar de Tag para qualquer arquivo que faça referência ao componente de exibição. Consulte Gerenciando o escopo do auxiliar de tag para obter mais informações sobre como registrar auxiliares de tag.

O InvokeAsync método usado neste tutorial:

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

Na marcação do Tag Helper:

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

No exemplo acima, o PriorityList componente de exibição torna-se priority-list. Os parâmetros para o componente de visualização são passados como atributos em kebab case.

Invocando um componente de exibição diretamente de um controlador

Os componentes de exibição normalmente são invocados a partir de uma vista, mas pode invocá-los diretamente de um método de controlador. Embora os componentes de exibição não definam pontos de extremidade como controladores, você pode implementar facilmente uma ação do controlador que retorna o conteúdo de um ViewComponentResult.

Neste exemplo, o componente de exibição é chamado diretamente do controlador:

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

Passo a passo: Criando um componente de exibição simples

Baixe, construa e teste o código inicial. É um projeto simples com um ToDo controlador que exibe uma lista de itens ToDo .

Lista de ToDos

Adicionar uma classe ViewComponent

Crie uma pasta ViewComponents e adicione a seguinte PriorityListViewComponent classe:

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();
        }
    }
}

Notas sobre o código:

  • As classes de componente de exibição podem estar contidas em qualquer pasta do projeto.

  • Como o nome da classe PriorityListViewComponent termina com o sufixo ViewComponent, o tempo de execução usa a cadeia de caracteres PriorityList ao fazer referência ao componente de classe de uma exibição.

  • O [ViewComponent] atributo pode alterar o nome usado para fazer referência a um componente de exibição. Por exemplo, a classe poderia ter sido nomeada XYZ com o ViewComponent atributo:

    [ViewComponent(Name = "PriorityList")]
       public class XYZ : ViewComponent
    
  • O [ViewComponent] atributo no código anterior informa ao seletor de componente de exibição para usar:

    • O nome PriorityList ao procurar os modos de exibição associados ao componente
    • A cadeia de caracteres "PriorityList" ao fazer referência ao componente de classe a partir de uma exibição.
  • O componente usa a injeção de dependência para disponibilizar o contexto de dados.

  • InvokeAsync expõe um método que pode ser chamado a partir de uma exibição, e pode ter um número arbitrário de argumentos.

  • O InvokeAsync método retorna o conjunto de ToDo itens que satisfazem os isDone parâmetros e maxPriority .

Criar o componente de vista Razor

  • Crie a pasta Views/Shared/Components . Esta pasta deve ser nomeada Components.

  • Crie a pasta Views/Shared/Components/PriorityList. Esse nome de pasta deve corresponder ao nome da classe do componente de exibição ou ao nome da classe menos o sufixo (se seguirmos a convenção e usarmos o sufixo ViewComponent no nome da classe). Se você usou o ViewComponent atributo, o nome da classe precisará corresponder à designação do atributo.

  • Crie uma Views/Shared/Components/PriorityList/Default.cshtmlRazor vista:

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

    O Razor recebe uma lista de TodoItem e as exibe. Se o método do componente InvokeAsync não passar o nome da vista (como no nosso exemplo), Padrão será usado para o nome da vista por convenção. Mais adiante no tutorial, mostrarei como passar o nome da exibição. Para substituir o estilo padrão de um controlador específico, adicione uma vista à pasta de vistas específica do controlador (por exemplo Views/ToDo/Components/PriorityList/Default.cshtml).

    Se o componente de exibição for específico do controlador, você poderá adicioná-lo à pasta específica do controlador (Views/ToDo/Components/PriorityList/Default.cshtml).

  • Adicione um div que contenha uma chamada para o componente da lista de prioridades na parte inferior do arquivo Views/ToDo/index.cshtml.

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

A marcação @await Component.InvokeAsync mostra a sintaxe para chamar componentes de exibição. O primeiro argumento é o nome do componente que queremos invocar ou chamar. Os parâmetros subsequentes são passados para o componente. InvokeAsync pode aceitar um número arbitrário de argumentos.

Teste o aplicativo. A imagem a seguir mostra a lista de ToDo e os itens prioritários:

Lista de tarefas e itens prioritários

Você também pode chamar o componente de exibição diretamente do controlador:

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

itens prioritários da ação IndexVC

Especificando um nome de modo de exibição

Um componente de exibição complexo pode precisar especificar um modo de exibição não padrão em algumas condições. O código a seguir mostra como especificar a exibição "PVC" do método InvokeAsync. Atualize o método InvokeAsync na classe 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);
}

Copie o Views/Shared/Components/PriorityList/Default.cshtml arquivo para um modo de exibição chamado Views/Shared/Components/PriorityList/PVC.cshtml. Adicione um título para indicar que a visualização PVC está a ser utilizada.

@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>

Atualização Views/ToDo/Index.cshtml:

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

Execute o aplicativo e verifique a visualização PVC.

Componente de visualização prioritária

Se a visualização PVC não estiver renderizada, verifique se você está chamando o componente de exibição com prioridade igual ou superior a 4.

Examinar o caminho de exibição

  • Altere o parâmetro priority para três ou menos para que a exibição de prioridade não seja retornada.

  • Renomeie temporariamente o Views/ToDo/Components/PriorityList/Default.cshtml para 1Default.cshtml.

  • Teste o aplicativo, você receberá o seguinte erro:

    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
    
  • Copie Views/ToDo/Components/PriorityList/1Default.cshtml para Views/Shared/Components/PriorityList/Default.cshtml.

  • Adicione alguma marcação ao componente de vista ToDo Partilhada para indicar que a vista é proveniente da pasta Partilhada.

  • Teste a vista Compartilhada do componente.

Saída de ToDo com visualização de componente compartilhado

Evitando cadeias de caracteres codificadas

Se quiser segurança no tempo de compilação, você pode substituir o nome do componente de exibição codificado pelo nome da classe. Crie o componente de visualização sem o sufixo "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();
        }
    }
}

Adicione uma using instrução ao seu Razor arquivo de exibição e use o nameof operador:

@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>

Você pode usar uma sobrecarga de Component.InvokeAsync método que usa um tipo CLR. Lembre-se de usar o typeof operador neste caso:

@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>

Executar trabalho síncrono

A estrutura lida com a invocação de um método síncrono Invoke se você não precisar executar trabalho assíncrono. O método a seguir cria um componente de exibição síncrona Invoke :

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

O arquivo do Razor componente de exibição lista as cadeias de caracteres passadas para o Invoke método (Views/Home/Components/PriorityList/Default.cshtml):

@model List<string>

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

O componente de exibição é invocado em um Razor arquivo (por exemplo, Views/Home/Index.cshtml) usando uma das seguintes abordagens:

Para usar a IViewComponentHelper abordagem, ligue para Component.InvokeAsync:

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

Para usar o Auxiliar de tag, registre o assembly que contém o componente View usando a @addTagHelper diretiva (o componente view está em um assembly chamado MyWebApp):

@addTagHelper *, MyWebApp

Use o componente de vista Tag Helper no arquivo de marcação Razor.

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

A assinatura do método PriorityList.Invoke é síncrona, mas Razor localiza e chama o método com Component.InvokeAsync no ficheiro de marcação.

Todos os parâmetros do componente de visualização são necessários

Cada parâmetro em um componente de exibição é um atributo obrigatório. Veja o sobre este problema do GitHub. Se algum parâmetro for omitido:

  • A InvokeAsync assinatura do método não corresponderá, portanto, o método não será executado.
  • O ViewComponent não renderizará nenhuma marcação.
  • Nenhum erro será gerado.

Recursos adicionais