> Manuales > Manual del framework ASP.NET MVC

Analizamos la sintaxis y sus consideraciones para el motor de vistas Razor en .NET.

Desde su versión ASP.NET MVC ha tenido el concepto de motor de vistas (View Engine). A ver, recapitulemos: en ASP.NET MVC las vistas realizan tareas sólo de presentación. No contienen ningún tipo de lógica de negocio y no acceden a datos. Básicamente se limitan a mostrar datos (en el artículo anterior vimos como pasar datos de los controladores a las vistas) y a solicitar datos nuevos al usuario. Si vienes del mundo de webforms, olvídate del concepto de Web Controls: no existen en ASP.NET MVC. No tenemos drag and drop, no configuramos propiedades. Las vistas son básicamente HTML. Y lo que no es HTML son pequeñas porciones de código de servidor destinadas a terminar generando HTML para mostrar información.

El equipo que desarrolló ASP.NET MVC tuvo la feliz idea de permitir separar la sintaxis de servidor usada, del framework de ASP.NET MVC. ¿El resultado? Lo que llamamos un motor de vistas de ASP.NET MVC. En las versiones 1 y 2, el framework viene con un único motor de vistas, el llamado motor ASPX, que usa los clásicos archivos .aspx como vistas de ASP.NET MVC. Pero ¡ojo! Aunque estemos utilizando archivos .aspx, no son webforms (los webcontrols no funcionan, no tenemos viewstate ni el ciclo de vida de webforms y además es que… son vistas de MVC, por lo que sólo deben hacer tareas de presentación). La sintaxis que usa el motor ASPX es un poco… eso que los anglosajones llaman verbose, es decir que se debe escribir mucho para ciertas tareas. Así, imaginad una página que tuviese que mostrar una lista de elementos de la clase Usuario (la que usamos en el capítulo anterior). El código, usando el motor de vistas ASPX queda así:

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<IEnumerable<MvcHelloWorld.Controllers.Usuario>>" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
DemoAspx
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<h2>DemoAspx</h2>
<table>
<% foreach (var item in Model) { %>
<tr>
<td>
<%: item.Nombre %>
</td>
<td>
<%: item.Twitter %>
</td>
<td>
<%: String.Format("{0:g}", item.Alta) %>
</td>
</tr>
<% } %>
</table>
</asp:Content>

Fijaos en la cantidad de veces que debe usarse el tag <% y su parejo %> para indicar dónde empieza y termina el código de servidor.

Rápidamente empezaron a surgir motores de vistas alternativos, realizados por la comunidad, con la intención de tener sintaxis más claras para nuestras vistas. Algunos ejemplos son Nhaml y Spark.

Finalmente la versión 3 de ASP.NET MVC vino acompañada de un nuevo motor de vistas, llamado Razor. Eso sí, el motor ASPX puede seguir siendo usado en ASP.NET MVC3, pero honestamente no hay ninguna razón para hacerlo (salvo en casos de migraciones, por supuesto): Razor es más claro, sencillo e intuitivo.

Sintaxis de Razor

Lo que más choca de Razor es que, a diferencia del motor ASPX donde tenemos el tag que inicia el código de servidor y el que lo termina, sólo hay tag para iniciar código de servidor. El motor Razor es lo suficientemente inteligente para saber cuándo termina el código de servidor, sin necesidad de que lo explicitemos. Veamos la misma vista de antes, pero ahora usando Razor:

@model IEnumerable<MvcHelloWorld.Controllers.Usuario>
@{
ViewBag.Title = "DemoRazor";
}
<h2>DemoRazor</h2>
@foreach (var item in Model) {
<tr>
<td>
@item.Nombre
</td>
<td>
@item.Twitter
</td>
<td>
@String.Format("{0:g}", item.Alta)
</td>
</tr>
}
</table>

Las diferencias saltan a la vista, ¿no? En Razor el símbolo de la arroba (@) marca el inicio de código de servidor. Y como comentaba antes, no hay símbolo para indicar que se termina el código de servidor: el motor Razor deduce cuando termina en base al contexto.

Fijaos que para mostrar una variable de servidor (item.Nombre p.ej.) simplemente la precedemos de una @. Fijaos también que la llave de cierre del foreach no debe ser precedida de ninguna arroba, Razor ya sabe que esa llave es de servidor y cierra el foreach abierto.

El uso de la @ funciona de dos maneras básicas:

  1. @expresión: Renderiza la expresión en el navegador. Así @item.Nombre muestra el valor de ítem.Nombre. Es decir @expresión equivale a <%: expresión %>
  2. @{ código }: Permite ejecutar un código que no genera salida HTML. Es decir @{código} equivale a <% Código %>

Consideraciones a la sintaxis

Expresiones compejas
Como hemos visto el motor Razor interpreta cuando empieza y cuando termina el código de servidor. Pero no siempre lo consigue adecuadamente. P.ej, el siguiente código Razor:

@{ int a = 10; int b = 3; }

El valor de 10 - 3 es: @a-b

Genera el siguiente HTML:

El valor de 10 - 3 es: 10-b

Es decir Razor ha interpretado que el código de servidor terminaba al encontrar el símbolo de resta. En este caso, esa presunción es totalmente errónea, pero por suerte podemos usar los paréntesis para que haya sólo una expresión detrás de la arroba:

El valor de 10 - 3 es: @(a-b)

Con ese código la vista mostrará el valor correcto (7). Recordad la clave: el motor Razor espera una y sólo una expresión detrás de la @. Por eso debemos usar los paréntesis.

"Romper" el código de servidor
A veces la problemática es justo la contraria de la que hemos visto con las expresiones complejas: a veces hay código que Razor interpreta que es de servidor pero realmente parte de ese código es HTML que debe enviarse al cliente. Veamos un ejemplo:

@for (int i = 0; i < 10; i++)
{
El valor de i es: @i <br />
}

A priori podríamos esperar que este código generara 10 líneas de código HTML. Pero el resultado es un error de compilación. La razón es que Razor interpreta que la línea "El valor de i es: @i" es código de servidor. Para "romper" este código de servidor y que Razor "sepa" que realmente esto es código que debe enviarse tal cual al cliente, tenemos dos opciones:

1. Intercalar una etiqueta HTML. Al intercalar una etiqueta HTML Razor "se da cuenta" que allí empieza un código de cliente:

@for (int i = 0; i < 10; i++)
{
<span>El valor de i es:</span> @i <br />
}

2. Usar la construcción @: que indica explícitamente a Razor que lo que sigue es código de cliente:

@for (int i = 0; i < 10; i++)
{
@:El valor de i es: @i <br />
}

La diferencia es que en el primer caso las etiquetas se envían al navegadaor, mientras que en el segundo caso no.

Consideraciones en correos electrónicos

La gente que ha desarrollado el motor Razor ha tenido en cuenta una excepción para los correos electrónicos. Es decir, Razor es de nuevo, lo suficientemente inteligente para saber que una expresión es un correo electrónico. Así pues el siguiente código no da error y envía el HTML esperado al cliente:

Enviame un mail: a usuario@servidor.com

En este caso Razor interpreta que se trata de un correo electrónico, por lo que no trata la @ como inicio de código de servidor. Este comportamiento a veces tampoco se desa. P.ej, imaginad lo siguiente:

<div id="div_@Model.Index">Div usado</div>

Estoy asumiendo que el ViewModel que recibe la vista tiene una propiedad llamada Index. Supongamos que dicha propiedad (Model.Index) vale 10. La verdad es que uno pensaría que eso generaría el código HTML:

<div id="div_10">Div usado</div>

Pero el resultado es bien distinto. El código HTML generado es:

<div id="div_@Model.Index">Div usado</div>

Es decir, Razor no ha interpretado la @ como inicio de código de servidor, y eso es porque ha aplicado la excepción de correo electrónico. La solución pasa por usar los paréntesis:

<div id="div_@(Model.Index)">Div usado</div>

Ahora Razor sabe que Model.Index es código de servidor y lo evaluará, generando el HTML que estábamos esperando.

A veces Razor falla incluso más espectacularmente. Dado el siguiente código:

@for (int i = 0; i <= 1; i++)
{
<div id="div_@i">Div @i</div>
}

En base a lo que hemos visto podríamos esperar que generase el siguiente HTML:

<div id="div_@i">Div 0</div>
<div id="div_@i">Div 1</div>

Pues ¡no! Este código hace que el motor de Razor de un error (The for block is missing a closing "}" character. Make sure you have a matching "}" character for all the "{" characters within this block, and that none of the "}" characters are being interpreted as markup).

Por supuesto, la solución para generar el HTML que queremos pasa por usar el paréntesis igual que antes:

@for (int i = 0; i <= 1; i++)
{
<div id="div_@(i)">Div @i</div>
}

Genera el HTML esperado:

<div id="div_0">Div 0</div>
<div id="div_1">Div 1</div>

Escapar la arroba
A veces es necesario indicarle a Razor que una arroba es eso… una simple arroba y que no haga nada específico. Que no asuma nada, que no piense nada, que simplemente envíe una @ al cliente. ¿Un ejemplo? El siguiente código:

<style>
@@media screen
{
body { font-size: 13px;}
}
</style>

Si no usáramos la doble arroba (@@), Razor nos generaría un error, ya que @media lo interpreta como "enviar el contenido de la variable media al cliente". En este caso al usar @@ Razor simplemente sabe que debe enviar una @ al cliente y lo que el navegador recibe es:

<style>
@media screen
{
body { font-size: 13px;}
}
</style>

Conclusiones

Hemos visto la sintáxis básica del motor de vistas Razor, y las principales consideraciones que debemos tener presentes. No hemos visto las capacidades adicionales de dicho motor de vistas como layouts, templates y regiones que iremos viendo en capítulos posteriores.

Para finalizar una cosilla: Ambos motores de vistas (ASPX y Razor) se pueden usar en MVC3 de forma simultánea. El motor de ASP.NET MVC intentará encontrar primero una vista .aspx y si no la encuentra buscará una vista Razor (.cshtml, aunque también puede ser .vbhtml si usamos Visual Basic.NET en lugar de C#). Aunque por supuesto este comportamiento puede ser modificado.
¡Un saludo a todos!

Eduard Tomàs

Apasionado de la informática, los videojuegos, rol y... la cerveza. Key Consulta...

Manual