(Este aqui é o quarto post sobre as novidades do Preview 5 (P5) do ASP.Net MVC. Mais informações aqui e aqui.)

Vou falar neste post de um conceito que já tinha umas sementes plantadas no Preview 4 e que é bem interessante: Partial Rendering.

Partial Rendering é a habilidade que você tem de pegar pedaços de interface gráfica e incluir na sua View. Por exemplo, você pode incluir um menu, um footer, um header, o que achar melhor. O conceito é meio parecido com web controls, mas não é a mesma coisa. Web controls, para o MVC, são simples geradores de html, são views tanto quanto uma página aspx. E os dois, páginas e web controls, podem ser usados como views, tanto para renderização completa, quanto para parcial.

Você pode confirmar isso criando uma ação simples, e, para a view, em vez de criar uma aspx, crie um ascx, com o mesmo padrão de nomes. Vai rodar e vai funcionar. A diferença é que controles não tem todo o código html por padrão, então vai sair sem <html>, sem <body>, sem <head>, etc. Mas vai sair. O ideal é, quando utilizar o view engine do webforms, utilizar controles para renderização parcial, e páginas para renderização completa.

E como funciona a renderização parcial? De forma muito simples. Você chama o método RenderPartial da classe HtmlHelper, passa o nome da view, opcionalmente também o modelo e o dicionário de dados da view. Se você não passar esses opcionais o ASP.Net sozinho vai passar os mesmos objetos que a view que está chamando recebeu. Ou seja, é bom verificar, porque se você não indicar qual o modelo, pode receber um erro.

Abaixo está o código da página Index.aspx, que vem com o MVC. Alterei ela para utilizar um modelo diferente, do qual já vou falar. Vejam a declaração da página:

public partial class Index : ViewPage<Models.ModeloComposto<DateTime>>

Abaixo as classes de modelo que criei:

public class ModeloComposto
{
    public IList<Aniversariante> AniversariantesDoMes { get; set; }
}

public class ModeloComposto<T> : ModeloComposto
{
    public T ModeloCustomizado { get; set; }
}

Vejam que criei um modelo composto, para facilitar. Estou assumindo que todas as views da aplicação dependem de um dado comum, que coloquei na classe ModeloComposto. Essa classe vai ser alterada conforme eu precise de mais dados, como um footer customizado, onde eu incluiria uma propriedade string. Neste exemplo estou assumindo que toda página teria um widgetzinho que exibiria os aniversariantes de hoje, e para isso ser possível, preciso ser capaz de passar essa informação à view, e é isso que estou fazendo.

Já a classe genérica ModeloComposto<T> estou usando para poder estender a classe que contém as propriedades compartilhadas. Por exemplo, minha view Index utiliza uma ModeloComposto<DateTime>, ou seja, o seu modelo mesmo é um objeto do tipo data, mas ela também recebe os outros comuns. Views simples, como a About, que não têm modelo próprio (são quase estáticas), utilizam a primeira classe. As que precisarem de modelo próprio utilizam a classe genérica.

Vejam o controlador como ficou:

public ActionResult Index()
{
    ViewData["Title"] = "Home Page";
    ViewData["Message"] = "Welcome to ASP.NET MVC!";
    var modelo = new Models.ModeloComposto<DateTime>()
    {
        AniversariantesDoMes = Models.Aniversariante.ObterAniversariantesDoMes(),
        ModeloCustomizado = DateTime.Now
    };
    return View(modelo);
}

Notem que crio a classe de modelo composto, e passo à ela a data atual. Na propriedade comum, de aniversariantes, passo os aniversariantes via um suposto método de negócio (simples, somente para exemplo). Vejam o resto da View:

<h3>
    Hoje é dia <% =ViewData.Model.ModeloCustomizado.ToShortDateString() %>
</h3>

<% Html.RenderPartial("Aniversarios", this.ViewData.Model.AniversariantesDoMes); %>

O modelo é uma simples data, e é utilizado, via propriedade ModeloCustomizado para exibir a data atual. Nada de mais aí.

O interessante mesmo é o método RenderPartial. Notem que passo a ele que quero renderizar parcialmente a View “Aniversarios”. Isso vai executar esta view e colocar o resultado desta view neste exato ponto. Era o que eu queria. Estou passando também os dados de aniversariantes do mês como modelo.

Vejam o código da ascx:

<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="Aniversarios.ascx.cs"
Inherits="MvcApplication2Preview5.Views.Home.Aniversarios" %>
<h5>Aniversariantes:</h5>
<table>
    <tr>
        <td>Nome</td>
        <td>Idade</td>
    </tr>
    <% foreach (var aniversariante in this.ViewData.Model)
       {
    %>
        <tr>
            <td><% =aniversariante.Nome %></td>
            <td><% =(DateTime.Now.Year - aniversariante.DataNascimento.Year)
                    .ToString() %></td>
        </tr>

    <%
       } %>
</table>

E logo abaixo a declaração da classe do controle:

public partial class Aniversarios : ViewUserControl<IList<Models.Aniversariante>>

Resumindo: o controle utiliza uma lista de aniversários, que recebe da view principal, que por sua vez recebe de uma view composta enviada pelo controlador. O controle não sabem que o está chamando, mas sabe o que fazer com o modelo, da mesma forma que a página aspx.

O resultado, simples, fica assim:

Página com resultado de view parcial

Bem interessante, certo? Tenho certeza que isso vai ajudar muito. E o legal é que o conceito de MVC e separação de responsabilidades está mantido. A responsabilidade das views continua sendo, somente, exibir os dados e interagir com o usuários, passando todo o controle aos controladores.

Preciso agora só abordar um aspecto interessante: quem deveria estar recebendo os dados na verdade não é cada View, mas a Master. E eu sei que a Master pode ser tipada por modelo também. Esse vai ser o caso de uso mais comum. Vou buscar isso e conto para vocês em seguida. Não tem a ver com o P5, mas é interessante. Falo de isso em breve. Até lá.

Giovanni Bassi

Arquiteto e desenvolvedor, agilista, escalador, provocador. É fundador e CSA da Lambda3. Programa porque gosta. Acredita que pessoas autogerenciadas funcionam melhor e por acreditar que heterarquia é mais eficiente que hierarquia. Foi reconhecido Microsoft MVP há mais de dez anos, dos mais de vinte que atua no mercado. Já palestrou sobre .NET, Rust, microsserviços, JavaScript, TypeScript, Ruby, Node.js, Frontend e Backend, Agile, etc, no Brasil, e no exterior. Liderou grupos de usuários em assuntos como arquitetura de software, Docker, e .NET.