Hola a todos!! Este post continua la mini-serie de posts que empecé con el que trataba sobre las áreas.
En este caso he estado jugando un poco con lo que llaman UI helper templating support y que me parece bastante interesante…
En el HtmlHelper han aparecido básicamente dos métodos nuevos: EditorFor<TM, TV> y DisplayFor<TM, TV>, que sirven para renderizar un editor o un visualizador para el tipo de datos TV del modelo cuyo tipo es TM.
Supongamos que tengo una clase Persona en mi modelo tal como:
public class Persona
{
public string Nombre { get; set; }
public int Edad { get; set; }
}
Bien, ahora en el controlador Home, en la acción Index, obtenemos un lista de objetos de dicha clase y la pasamos a la vista:
public ActionResult Index()
{
var matusalen = new Persona()
{
Nombre = "Matusalén",
Edad = 350
};
var jacob = new Persona()
{
Nombre = "Jacob",
Edad = 32
};
ViewData.Model = new List<Persona>() { matusalen,jacob};
return View();
}
En ASP.NET MVC 1.0 para mostrar los elementos tendríamos un código en la vista similar a (suponiendo una vista tipada a IEnumerable<Persona>):
<table>
<tr><th>Nombre</th><th>Edad</th></tr>
<% foreach (var persona in this.Model)
{ %>
<tr>
<td><%= Html.Encode(persona.Nombre) %></td>
<td><%= Html.Encode(persona.Edad) %></td>
</tr>
<% } %>
</table>
Algunos otros a quienes no les gustan tantos tags de servidor mezclados con código cliente se crearían una vista parcial tipada a Persona, para mostrar los datos de UNA persona:
<%@ Control Language="C#"
Inherits="System.Web.Mvc.ViewUserControl<MvcApplication1.Models.Persona>" %>
<tr>
<td><%= Html.Encode(Model.Nombre) %></td>
<td><%= Html.Encode(Model.Edad) %></td>
</tr>
Y una vez tienen esta vista parcial (llamada p.ej. PersonaView) en la vista principal tendrían un código mucho más simple:
<table>
<tr><th>Nombre</th><th>Edad</th></tr>
<% foreach (var persona in this.Model)
Html.RenderPartial("PersonaView", persona);
%>
</table>
Esta técnica (de usar una vista parcial) es la que está recomendada (especialmente si nuestro modelo tiene muchos campos)…
… y ASP.NET MVC 2, “integra” esa forma de pensar con los UI templates.
En ASP.NET MVC 2, en lugar de utilizar directamente Html.Encode para mostrar una propiedad, podemos utilizar Html.DisplayFor, que nos renderizará un “visualizador” para una propiedad de nuestro modelo. Y que tipo de visualizador renderizará? Pues para propiedades simples (tales como bools, enteros, strings) es capaz de generar el control HTML correspondiente.
P.ej. el primer paso que podríamos hacer es cojer la vista parcial PersonaView que tenemos y cambiar las llamadas a Html.Encode por llamadas a Html.DisplayFor:
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<MvcApplication1.Models.Persona>" %> <tr> <td><%= Html.DisplayFor(x => x.Nombre) %></td> <td><%= Html.DisplayFor(x => x.Edad) %></td> </tr>
Fijaos en la sintaxis de DisplayFor: se pasa una lambda expresion que indica que se debe renderizar (puede ser un objeto o una propiedad del modelo). Pasar la lambda tiene un par de ventajas respecto a pasar el valor (como le pasábamos en el Encode), y es que entonces podemos utilizar reflection para inspeccionar la propiedad o clase que recibimos y ver si está decorada con determinados atributos… esto es lo que se llama DataAnnotation Support y sería tema para otro post 😉
Bueno… la verdad es que de momento, alguien quizá se está preguntando que mejora nos aporta esto, porque más o menos tenemos un código muy parecido. La mejora viene ahora. Hasta aquí hemos utilizado DisplayFor para tipos sencillos, pero también podemos utilizarlo para tipos complejos… Es decir podemos pasar todo un objeto Persona en el DisplayFor y dejar que ASP.NET MVC decida que “visualizador” debe utilizar. Podemos modificar la vista principal para que quede:
<table>
<tr><th>Nombre</th><th>Edad</th></tr>
<% foreach (var persona in this.Model)
Html.DisplayFor(x => persona);
%>
</table>
Fijaos que ahora en cada llamada a DisplayFor, no le estamos pasando una propiedad del modelo (en este caso mi modelo es un IEnumerable<Persona>), sinó que le pasamos el objeto persona a renderizar. Si ahora ejecutamos nuestro código… primera decepción, ya que vemos algo como:
¿Que ha pasado? Pues simple: estamos viendo el comportamiento por defecto de DisplayFor cuando recibe un tipo completo: Itera sobre todas las propiedades y va mostrando su nombre y su valor… Útil, lo que se dice útil no lo es mucho… Nos queda un último paso que es configurar un UI Template para el tipo Persona, es decir indicarle a ASP.NET MVC como debe renderizar un visualizador para un objeto de tipo Persona.
Los visualizadores son siempre vistas parciales normales y corrientes (no deben implementar ninguna interfaz ni nada). En nuestro caso vamos a utilizar la vista parcial PersonaView que creamos antes. Los UI Templates en ASP.NET MVC siguen la regla de “convención antes que configuración”, eso significa que no debemos “registrarlos” en ningun sitio, sinó que implemente ASP.NET MVC va a buscarlos en una carpeta determinada… que se llama de DisplayTemplates y que está dentro de Views/Shared (para templates compartidos entre todos los controladores) o Views/<Controler> (para templates que aplican a un solo controlador).
En nuestro caso creamos la carpeta “DisplayTemplates” dentro de la carpeta Views/Home y movemos la vista PersonaView.ascx dentro de la carpeta DisplayTemplates y la renombramos como Persona.ascx (para que se llame igual que el tipo que renderiza).
Ahora si ejecutamos de nuevo la aplicación, vemos que ya se muestran bien los datos: ASP.NET MVC ha encontrado el UI Template para el tipo persona y lo ha utilizado!
Si no queremos renombrar la vista (y que se siga llamando PersonasView) entonces cuando utilizamos el DisplayFor debemos indicarle que template se debe utilizar:
Html.DisplayFor(x => persona, "PersonaView");
En el caso de Html.EditorFor todo funciona exactamente igual, salvo que la carpeta para los templates no se llama DisplayTemplates sinó EditorTemplates.
Un saludo!!!