No último post eu mostrei como customizar a renderização de um campo DateTime do seu Model através do MVC Futures (para saber mais, clique aqui). Neste post vou mostrar como utilizar o mesmo mecanismo para renderizar tipos complexos.
Modelo
No modelo abaixo demonstrado temos duas classes bastante simples: ListaPresenca e Aluno, onde a classe ListaPresenca possui uma coleção de objetos do tipo Aluno:
MVC Futures
Por convenção, ao utilizar o helper Html.EditorFor, o ASP.NET MVC procura dentro da pasta Views\Shared\EditorTemplates algum arquivo .ascx que tenha o mesmo nome do tipo de dado que está sendo renderizado. Por exemplo, quando utilizamos o comando Html.EditorFor(m => m.NomeInstrutor), o framework vai procurar o template String.ascx e utilizá-lo para renderizar a propriedade NomeInstrutor, pois ela é do tipo String. Caso não exista nenhum .ascx com o nome do tipo a ser renderizado, o framework utiliza o template Object.ascx para renderizar.
View
Dado o cenário acima, a idéia é que nossa View renderize um formulário para atualizar os dados dos alunos de uma determinada turma, através da classe ListaPresenca. O código da View está demonstrado abaixo:
@model TesteTemplate.Models.ListaPresenca @{ ViewBag.Title = "Index"; } <h2>Index</h2> <script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script> <script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script> @using (Html.BeginForm()) { @Html.ValidationSummary(true) <fieldset> <legend>ListaPresenca</legend> <div class="editor-label"> @Html.LabelFor(model => model.NomeInstrutor) </div> <div class="editor-field"> @Html.EditorFor(model => model.NomeInstrutor) @Html.ValidationMessageFor(model => model.NomeInstrutor) </div> <div class="editor-field"> @Html.EditorFor(model => model.Alunos, "CollectionAlunos") </div> <p> <input type="submit" value="Save" /> </p> </fieldset> }
Na linha 25 estamos dizendo ao ASP.NET MVC para utilizar o template CollectionAlunos.ascx para renderizar a propriedade Alunos da classe ListaPresenca. Esse template ainda não existe em nosso projeto, portanto será utilizado o template Object.ascx. Para que isso funcione, basta criarmos o CollectionAlunos.ascx dentro da pasta Views\Shared\EditorTemplates de nosso projeto. O código do template (bem simples) será algo mais ou menos assim:
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %> <table> <thead> <% if (Model != null) { %> <tr> <th>Nome</th> <th>Endereço</th> </tr> </thead> <tbody> <% string oldPrefix = ViewData.TemplateInfo.HtmlFieldPrefix; int index = 0; ViewData.TemplateInfo.HtmlFieldPrefix = String.Empty; foreach (Object item in (IEnumerable)Model) { string fieldName = String.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}[{1}]", oldPrefix, index++); ViewContext.Writer.Write(Html.EditorFor(m => item, null, fieldName)); } ViewData.TemplateInfo.HtmlFieldPrefix = oldPrefix; } %> </tbody> </table>
Na linha 22, estamos utilizando o comando Html.EditorFor(m => item, null, fieldName). Este item que está sendo passado para o Helper é um objeto do tipo Aluno, como vocês podem ver na imagem abaixo:
No entanto, não estamos dizendo para o framework qual controle .ascx ele deve utilizar (estamos passando null no segundo parâmetro). Por convenção, nesse caso, ele assume que deve ser utilizado o Aluno.ascx ou Object.ascx. Vamos então criar o template Aluno.ascx dentro da pasta Views\Shared\EditorTemplates:
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<TesteTemplate.Models.Aluno>" %> <tr> <td> <% ViewContext.Writer.Write(Html.EditorFor(m => m.Nome)); %> </td> <td> <% ViewContext.Writer.Write(Html.EditorFor(m => m.Endereco)); %> </td> </tr>
Feito isso, o resultado será algo do tipo:
Quando clicamos no botão Save, realizamos um Post. No controller, ao verificar o objeto ListaPresenca, podemos ver que o mesmo foi devidamente populado através do Model Binding do ASP.NET MVC:
Quando isso é útil? Quando temos um objeto complexo que será renderizado em várias Views do mesmo software. Ao invés de duplicamos código para fazer a renderização desse tipo em cada uma das Views, basta criar um template para renderizá-lo e utilizar o helper Html.EditorFor.
Espero que tenham gostado da dica, até a próxima.
Osmar Landin