in ASP.NET

ASP.NET MVC 6 – View Components

Um conceito novo que o ASP.NET MVC 6 traz é o View Components, cuja ideia principal é ser algo parecido com uma partial view, porém com muito mais recursos como testabilidade, isolamento de conceitos. Basicamente é possível fazer uma comparação como um mini controller, que é responsável pela renderização de um bloco só da pagina. Exemplos de uso: dados do usuário, menus customizados, informações de últimos produtos, promoções, algo que possua uma lógica e implementação um pouco mais complexa que uma Partial View.

A estrutura básica de um View Component é composta de uma classe (geralmente que herde de ViewComponent) e um arquivo Razor.

Classe

Uma classe de View Component pode ser criada e configurada das seguintes maneiras:

  • Herdando de ViewComponent
  • Decorando a classe com o atributo [ViewComponent], ou herdando de uma classe que tenha este atributo
  • Criando uma classe com o padrão de nome que termine com ViewComponent. Ex: UltimasNoticiasViewComponent

Para este exemplo, criei um projeto no Visual Studio 2015 Preview e vou adicionar a seguinte classe UltimasNoticias.cs:

using System;
using Microsoft.AspNet.Mvc;
using WebApplication1.Models;
using System.Linq;

namespace WebApplication1.ViewComponents
{
    public class UltimasNoticias : ViewComponent
    {
        private readonly ApplicationDbContext db;

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

        public IViewComponentResult Invoke(int total)
        {
            ////ex do context, como é possível acessar
            //var headers = Context.Request.Headers;

            var noticias = db.Noticias.Take(total).ToList();

            return View(noticias);
        }

    }
}

 

Outro detalhe: a classe pode estar em qualquer pasta do projeto, neste exemplo vou adicionar dentro de uma pasta chamada ViewComponents.

Razor

Para adicionar a view que vamos utilizar é necessário criar uma pasta chamada Components, no meu caso vou criar ela dentro de Views\Shareds\Components, além disto é necessário criar uma pasta com o nome que demos para a classe (se utilizarmos o sufixo da convenção, ele não é necessário): UltimasNoticias. Deixando a seguinte estrutura Views\Shared\Components\UltimasNoticias.

Nesta pasta podemos criar o Default.cshtml, que será utilizado para renderizar as informações.

Deixando o projeto da seguinte maneira:

image

 

 

No arquivo Default.cshtml fiz a seguinte implementação:

@model IEnumerable<WebApplication1.Models.Noticia>

<h2>Noticias</h2>
<ul>
    @foreach (var noticia in Model)
    {
        <li>@noticia.Titulo</li>
    }
</ul>

 

Invocando em uma View

Para renderizar a View Component, precisamos utilizar a seguinte sintax (neste exemplo foi na Home/Index.cshtml

@{
    ViewBag.Title = "Home Page";
}

<div class="jumbotron">
    <h1>ASP.NET 5</h1>
    <p class="lead">ASP.NET is a free web framework for building great Web sites and Web applications using HTML, CSS and JavaScript.</p>
    <p><a href="http://asp.net/vnext" class="btn btn-primary btn-lg">Learn more &raquo;</a></p>
</div>

<div class="row">
    <div class="col-md-4">
        @Component.Invoke("UltimasNoticias", 3)
    </div>
</div>

E pronto, temos a View Component funcionando \o/

image

 

Invocando em uma View de maneira Async

Além disto, um feature bem legal de melhoria do Razor é o suporte ao Async, com isto, podemos renderizar esta View Componet de maneira assíncrona 🙂

Para isto vamos fazer algumas mudanças na nossa classe, deixando ela da seguinte maneira (retornando uma Task)

using System;
using Microsoft.AspNet.Mvc;
using WebApplication1.Models;
using System.Linq;
using System.Threading.Tasks;

namespace WebApplication1.ViewComponents
{
    public class UltimasNoticias : ViewComponent
    {
        private readonly ApplicationDbContext db;

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

        public async Task<IViewComponentResult> InvokeAsync(int total)
        {
            var noticias = await db.Noticias.Take(total).ToListAsync();

            return View(noticias);
        }

    }
}

Também precisamos mudar a chamada na nossa Index.cshtml:

@{
    ViewBag.Title = "Home Page";
}

<div class="jumbotron">
    <h1>ASP.NET 5</h1>
    <p class="lead">ASP.NET is a free web framework for building great Web sites and Web applications using HTML, CSS and JavaScript.</p>
    <p><a href="http://asp.net/vnext" class="btn btn-primary btn-lg">Learn more &raquo;</a></p>
</div>

<div class="row">
    <div class="col-md-4">
        @await Component.InvokeAsync("UltimasNoticias", 3)
    </div>
</div>

Com isto temos a nossa View Component sendo renderizada de maneira assíncrona.

Bom espero que este post seja útil, claro que muita coisa ainda pode mudar na versão final, mas acredito que faça parte do aprendizado ir conhecendo os novos conceitos e construindo de uma forma gradual o conhecimento.

Estou a disposição para dúvidas, criticas e sugestões.

abs

Rodolfo

  • Anonymous

    A ideia é muito boa, mas, já que estão reescrevendo tudo, eles poderiam ter feito outra maneira de chamar o componente. String mágica é tenso. 🙁

    Podia ser assim:

    @Component.Invoke(3)

  • http://xamarinbr.azurewebsites.net/ Angelo Belchior

    Mas pelo o que eu entendi, é possível ter vários métodos dentro de uma ViewComponent…

  • Anonymous

    Sim, mas quem renderiza a partial é o Invoke

  • Renato Saito

    Acredito que você pode criar um extension method com essa funcionalidade.

  • Anonymous

    ou implementar e mandar um pull request 😀

  • http://xamarinbr.azurewebsites.net/ Angelo Belchior

    Ou implementar e mandar um pull request!

  • http://xamarinbr.azurewebsites.net/ Angelo Belchior

    Eu não poderia ter um método chamado Renderizar por exemplo ao invés do Invoke? Ainda não testei…

  • Anonymous

    ahhhh, justo. achei que o Invoke fosse um override de ViewComponent.

    Então precisaria adaptar um pouco:

    @Component.Invoke((u) => u.Invoke(3))

  • http://www.rodolfofadino.com.br/ Rodolfo Fadino

    Acho que na versão final vai mudar a maneira de chamar o @Component.Invoke

  • Eduardo Spaki

    blz… mas qual a diferença entre uma chamada normal e async?

  • Guto Costa

    Normal acontece uma embaixo da outra, async acontecem ao mesmo tempo.

  • Renato Saito

    Interessante, você injetou a dependência ApplicationDbContext no construtor do ViewComponent?

  • Renato Saito

    Me respondendo, a injeção é feita pelo novo mecanismo de dependency injection. http://blogs.msdn.com/b/webdev/archive/2014/06/17/dependency-injection-in-asp-net-vnext.aspx

  • Fernando Mondo

    O que difere isso de um @Html.Action ?

  • http://www.rodolfofadino.com.br/ Rodolfo Fadino

    Fernando o Html.Action vc executa uma Action de um controller, passando por tudo que é inerente a isto: roteamento, filtros, binding, etc.

  • Fernando Mondo

    Foi exatamente o que eu supus, só não tinha achado nenhuma referência a isso.
    Obrigado.