Helpers e internalización - Servicio de Informática

Anuncio
SERVICIO DE INFORMÁTICA | UNIVERSIDAD DE ALICANTE
ASP.NET MVC 3 y 4
PERSONALIZACIÓN
Andrés Vallés Botella | Analista | Desarrollos propios
Servicio de Informática | Universidad de Alicante
Campus de Sant Vicent del Raspeig | 03690 | España
http://si.ua.es/es/
ÚLTIMA ACTUALIZACIÓN: 13 DE OCTUBRE DE 2012
ASP.NET MVC 3 Y 4 | DESARROLLO DE APLICACIONES CON EL FRAMEWORK MVC 3 Y 4
3º DÍA – PERSONALIZACIÓN
ENTITY FRAMEWORK BÁSICO
Vamos a estudiar lo básico para hacer consultas con Entity Framework.
Nos creamos en la aplicación de libros con Oracle, en el controlador Home, el método BD.
Los pasos para hacer la consulta son los siguientes:
1. Preparar una variable con el listado de datos que vamos a pasar al modelo. Si queremos un
listado de libros, usaremos IEnumerable<Tabla> para que desde la vista podamos recorrer los
datos con las plantillas básicas que nos ofrece MVC.
2. Abrir la conexión a la Entidad. Dependerá del nombre que se le haya puesto a la Entidad cuando
hayamos importado el modelo. Lo más cómodo es usar using([declaración conexión]) {} porque
se elimina automáticamente al salir del using.
3. Hacer la consulta y almacenar los resultados en la variable del punto 1. Lo más sencillo es
obtener todos los registros de una tabla [conexión].[nombretabla].ToList(). Las tablas pasan a
ser unos objetos más con los que podemos trabajar.
4. Cerrar o liberar la conexión con la entidad. Si hemos usado using no será necesario, pero en
cualquier otro caso habrá que usar bd.Dispose()
5. Llamar a la vista con el modelo de datos que hemos almacenado en el punto 1 y 3. Igual que
hemos hecho en otras ocasiones View([Variable]).
Podría ser algo así.
public ActionResult BD()
{
IEnumerable<CSI_LIBRO> libros;
using (var bd = new EntitiesBiblioteca())
{
libros = bd.CSI_LIBRO.ToList();
}
return View(libros);
}
La nomenclatura usada para esta consulta es lambda, que para es la más sencilla, pero la tradicional
de consultas (Linq) sería:
libros = (from l in bd.CSI_LIBRO
select l).ToList();
Como son muy sencilla ambas, que cada uno seleccione la que le sea más sencilla.
Ahora creamos una vista para este método y le indicamos el modelo CSI_LIBRO y la plantilla List.
Andrés Vallés Botella | Analista | Desarrollos propios
Servicio de Informática | Universidad de Alicante
Campus de Sant Vicent del Raspeig | 03690 | España
http://si.ua.es/es
2
ASP.NET MVC 3 Y 4 | DESARROLLO DE APLICACIONES CON EL FRAMEWORK MVC 3 Y 4
Si lo ejecutamos vemos el listado de libros. También incluye la creación de nuevos libros, edición,
borrado. Toda esta parte la podemos eliminar.
FILTRADO
Cuando hacemos consulta lo normal es mostrar aquellos que cumplen una condición.
La más básica podría ser la consulta por id
libros = bd.CSI_LIBRO.Where(l => l.ID == 1 ).ToList();
libros = (from l in bd.CSI_LIBRO
where l.ID == 1
select l).ToList();
O que esté en un intervalo
libros = bd.CSI_LIBRO.Where(l => l.ID >= 1 && l.ID < 3).ToList();
libros = (from l in bd.CSI_LIBRO
where l.ID >= 1 && l.ID <= 3
select l).ToList();
Si queremos consultar aquello libros que tienen en el título “El Principito”
libros = bd.CSI_LIBRO.Where(l => l.TITULO == "El Principito").ToList();
libros = (from l in bd.CSI_LIBRO
where l.TITULO == "El Principito"
select l).ToList();
O que contienen la palabra “Principito”
libros = bd.CSI_LIBRO.Where(l => l.TITULO.Contains("Principito") ).ToList();
libros = (from l in bd.CSI_LIBRO
where l.TITULO.Contains("Principito")
select l).ToList();
Andrés Vallés Botella | Analista | Desarrollos propios
Servicio de Informática | Universidad de Alicante
Campus de Sant Vicent del Raspeig | 03690 | España
http://si.ua.es/es
3
ASP.NET MVC 3 Y 4 | DESARROLLO DE APLICACIONES CON EL FRAMEWORK MVC 3 Y 4
Podemos hacer conversiones de mayúsculas o minúsculas de cualquier campo en la propia consulta
libros = bd.CSI_LIBRO.Where(l => l.TITULO.ToUpper().Contains("PRINCIPITO") ).ToList();
libros = (from l in bd.CSI_LIBRO
where l.TITULO.ToUpper().Contains("PRINCIPITO")
select l).ToList();
ORDENACIÓN
Disponemos de dos funciones en lambda OrderBy y OrderByDescending al que debemos indicar el
campo de ordenación
libros = bd.CSI_LIBRO.OrderBy(l => l.TITULO).ToList();
libros = (from l in bd.CSI_LIBRO
orderby l.TITULO
select l).ToList();
libros = bd.CSI_LIBRO.OrderByDescending(l => l.TITULO).ToList();
libros = (from l in bd.CSI_LIBRO
orderby l.TITULO descending
select l).ToList();
Si queremos ordenar por más de un campo, en lambda debemos usar ThenBy o ThenByDescending,
mientras que en las consultas tradicionales lo hacemos separando los campos por comas.
libros = bd.CSI_LIBRO.OrderBy(l => l.TITULO).ThenBy(l => l.ISBN).ToList();
libros = (from l in bd.CSI_LIBRO
orderby l.TITULO, l.ISBN
select l).ToList();
PAGINACIÓN
Uno de los problemas que nos encontramos cuando trabajamos con Oracle es la paginación. Obtener
un número limitado de registros y saltar hasta un determinado registro es trivial en EF.
Para obtener los 2 primeros registos de una consulta
libros = bd.CSI_LIBRO.OrderBy(l => l.TITULO).Take(2).ToList();
libros = (from l in bd.CSI_LIBRO
orderby l.TITULO, l.ISBN
select l).Take(2).ToList();
Si queremos saltar los primeros 10 registros y obtener los 2 primeros registros
libros = bd.CSI_LIBRO.OrderBy(l => l.TITULO).Skip(10).Take(2).ToList();
libros = (from l in bd.CSI_LIBRO
orderby l.TITULO, l.ISBN
select l).Skip(10).Take(2).ToList();
Andrés Vallés Botella | Analista | Desarrollos propios
Servicio de Informática | Universidad de Alicante
Campus de Sant Vicent del Raspeig | 03690 | España
http://si.ua.es/es
4
ASP.NET MVC 3 Y 4 | DESARROLLO DE APLICACIONES CON EL FRAMEWORK MVC 3 Y 4
HELPERS (BÁSICO)
HTMLHELPERS
Ayer vimos las funciones relacionadas con las direcciones. Hoy nos centramos en las que realmente
nos van a ayudar en el trabajo diario, las que nos generan código HTML.
Aunque el listado es mucho más amplio que el de URLHelpers, lo cierto es que todos son muy
parecidos.
Vamos a comenzar con el elemento básico que es el formulario que recoge todos los campos con los
que trabajamos.
Nos creamos una método / acción en nuestro controlador para ir jugando. Le podemos llamar
helpers. No le vamos a pasar ningún modelo por el momento.
Editamos su vista y lo básico para un formulario en HTML es <form …></form>.
La idea es que no lo escribamos directamente sino que usemos los helpers.
BeginForm(s:action, s:controller, o:values)
Si escribimos
@using (Html.BeginForm()) {
}
Nos generaría el siguiente código
<form action="/Home/helpers" method="post"></form>
Por defecto nos genera una acción a nosotros mismos y hace la llamada con método post
Con los parámetros podemos indicar que controlador / acción del action y el método
Html.BeginForm("Create", "Libro", FormMethod.Get)
Y generaría
<form action="/Libro/Create" method="get"></form>
Todos los HTMLHelpers incluyen un último parámetro abierto a añadir cualquier atributo HTML, por
ejemplo id, class, etc. En el caso de que queramos indicar el id
Html.BeginForm("Create", "Libro", FormMethod.Get, new {id="fTest"}))
Si además quisiéramos añadir el estilo con class al ser una palabra reservadas del sistema debemos
escribir la propiedad con @class.
Html.BeginForm("Create", "Libro", FormMethod.Get, new {id="fTest", @class="formulario-ua"})
El resultado final sería
<form action="/Libro/Create" class="formulario-ua" id="fTest" method="get"></form>
Andrés Vallés Botella | Analista | Desarrollos propios
Servicio de Informática | Universidad de Alicante
Campus de Sant Vicent del Raspeig | 03690 | España
http://si.ua.es/es
5
ASP.NET MVC 3 Y 4 | DESARROLLO DE APLICACIONES CON EL FRAMEWORK MVC 3 Y 4
En caso de que no usemos using en la declaración de beginform deberemos usar endform para
indicar donde acaba.
@{ Html.BeginForm("Create", "Libro", FormMethod.Get, new {id = "fTest", @class = "formulario-ua"}); }
…
@{ Html.EndForm();}
Yo usaré en todos los ejemplos using porque queda el código más agrupado.
Ahora es el momento de incluir elementos en el formulario
Label(s:name, o:text)
Un etiqueta con texto que hace referencia a un campo (name). En caso de que no se indique el texto
pondrá por defecto el nombre del campo que hayamos indicado en el primer parámetro.
@using(Html.BeginForm("Create", "Libro", FormMethod.Get, new {id = "fTest", @class = "formulario-ua"}))
{
@Html.Label("Nombre", "Nombre:")
…
}
En este ejemplo mostrará una etiqueta “Nombre:” que hace referencia a un campo “Nombre”.
<label for="Nombre">Nombre:</label>
TextBox(s:name, o:value)
Crea una caja de texto con el nombre que le indiquemos y con un valor por defecto en el segundo
parámetro.
@Html.TextBox("Nombre", "Alberto")
genera
<input id="Nombre" name="Nombre" type="text" value="Alberto" />
Todo el contenido que se asigne en el valor por defecto (en cualquier HTMLHelper) se codifica
automáticamente para evitar que se produzcan ataques XSS injection.
CheckBox(s:name, b:checked)
Genera una elemento checkbox.
DropDownList(s:name, list:selectlistitems)
Genera una lista desplegable en la que podemos seleccionar un único elemento.
@Html.Label("Sexo")
@Html.DropDownList("Sexo", new MultiSelectList(new[] {"Hombre", "Mujer"}))
Genera
Andrés Vallés Botella | Analista | Desarrollos propios
Servicio de Informática | Universidad de Alicante
Campus de Sant Vicent del Raspeig | 03690 | España
http://si.ua.es/es
6
ASP.NET MVC 3 Y 4 | DESARROLLO DE APLICACIONES CON EL FRAMEWORK MVC 3 Y 4
Hidden(s:name, o:value)
Genera un campo oculto.
ListBox(s:name, list:selectlistitems)
Genera una lista de valores en la que podemos seleccionar más de un elemento.
@using(Html.BeginForm("Create", "Libro", FormMethod.Get, new {id = "fTest", @class = "formulario-ua"}))
{
@Html.Label("Unidad")
@Html.ListBox("Unidad", new MultiSelectList(new[] {"Servicio de Informática", "Selección y Formación", "Servicio de
Personal"}))
}
Genera
RadioButton(s:name, o:value, b:checked)
Genera una elemento radiobutton
TextArea(s:name, s:value)
Crea una caja de texto de tipo textarea.
INCLUIR BUSCADOR EN NUESTRA APLICACIÓN
Con lo visto anteriormente preparar una caja de búsqueda totalmente funcional que busque en el
listado de libros. Se podrá buscar por título o por Isbn.
En caso de que se deje en blanco el término de búsqueda mostrará todos los libros.
Crearemos los modelos que necesitemos, un nuevo método para el controlador Home y una vista
con la caja de búsqueda.
Luego crearemos una 2º versión que incluiremos el buscador como una sección dentro de la plantilla
_layout.cshtml. De esta manera podremos indicar en las vistas si queremos incluirlo o no.
Andrés Vallés Botella | Analista | Desarrollos propios
Servicio de Informática | Universidad de Alicante
Campus de Sant Vicent del Raspeig | 03690 | España
http://si.ua.es/es
7
ASP.NET MVC 3 Y 4 | DESARROLLO DE APLICACIONES CON EL FRAMEWORK MVC 3 Y 4
VISTAS POR DEFECTO
Como ya se comentó el primer día las plantillas que usa el Visual Studio son personalizables. Se
almacena por defecto en Visual Studio 2010 en la carpeta
C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\ItemTemplates\
CSharp\Web\MVC 3\CodeTemplates\AddView\CSHTML
Si accedemos veremos 6 ficheros con la extensión tt. Lo primero que podemos pensar es en
modificarlo directamente, pero esto afecta a todos los proyectos. Además está en una carpeta que es
muchas ocasiones, y por medidas de seguridad, nos pide permisos de administrador.
Lo mejor es coger la carpeta CodeTemplates y copiarla a la raíz de nuestra aplicación (podemos
borrar todo lo que no sea AddView\CSHTML).
El siguiente paso para poder hacer modificaciones a la plantilla es cambiar el parámetro Herramienta
personalizada a las 6 plantillas. Por defecto tiene el valor TextTemplatingFileGenerator. Lo dejamos
vacío.
Andrés Vallés Botella | Analista | Desarrollos propios
Servicio de Informática | Universidad de Alicante
Campus de Sant Vicent del Raspeig | 03690 | España
http://si.ua.es/es
8
ASP.NET MVC 3 Y 4 | DESARROLLO DE APLICACIONES CON EL FRAMEWORK MVC 3 Y 4
¿Podemos crear nuevas plantillas y que aparezcan al crear una vista?
Por supuesto. Vamos a duplicar la plantilla para a vista de listado (list.tt) para que no incluya ni dar
de alta, ni poder gestionar los registros.
Aunque el código no es tan legible como en Razor, buscamos los bloques de HTML y vamos
eliminando los contenidos que nos interesen. El objetivo es que se vea un listado con este formato.
Un problema que nos podemos encontrar es que en los listados personalizados el tipo de libro
aparezca como código y no como descripción. Debemos realizar unos cambios.
En el controlador, hacer un include la tabla relacionada.
public ActionResult Buscar(string palabra)
{
IEnumerable<CSI_LIBRO> libros;
using (var bd = new EntitiesBiblioteca())
{
libros = bd.CSI_LIBRO.Include("CSI_TIPOLIBRO");
if (!String.IsNullOrEmpty(palabra))
{
libros = libros.Where(l => l.TITULO.ToUpper().Contains(palabra.ToUpper()));
}
libros = libros.ToList();
}
return View(libros);
}
En la vista cambiar el campo que es clave ajena por la [tabla relación].[campo descripción] (sólo en el
caso que hayamos realizado el include porque si no es inaccesible).
<td>
@Html.DisplayFor(modelItem => item.CSI_TIPOLIBRO.DESCRIPCION)
</td>
HELPERS
HTMLHELPERS (CONTINUACIÓN)
ValidationSummary([Exclude property-level error])
Muestra una lista no ordenada de todos los errores que se producen al validar el formulario.
Se puede validar todo o excluir los errores a nivel de las propiedades del modelo
Los errores se pueden lanzar en tiempo de ejecución con la propiedad AddModelError(campo,
mensaje de error) del objeto ModelState.
En caso de que el campo lo dejemos vacío estamos lanzando un error a nivel de modelo
ModelState.AddModelError("", "Prueba de un error general");
y si indicamos el campo lo hacemos a nivel de propiedad.
ModelState.AddModelError("Nombre", "El nombre no cumple con los requisitos");
Andrés Vallés Botella | Analista | Desarrollos propios
Servicio de Informática | Universidad de Alicante
Campus de Sant Vicent del Raspeig | 03690 | España
http://si.ua.es/es
9
ASP.NET MVC 3 Y 4 | DESARROLLO DE APLICACIONES CON EL FRAMEWORK MVC 3 Y 4
Añadimos a la vista un sumario de validación
@using(Html.BeginForm())
{
@Html.ValidationSummary(false)
}
El resultado
Por defecto asigna el estilo "validation-summary-errors”.
ValidationMessage (s:[Nombre del campo], o:[Mensaje de error])
En caso de que no queramos que sea un sumario el que recoja todos los mensajes, si no que cada
mensaje aparezca en el punto que indiquemos (normalmente a la derecha del campo), usaremos
este Helper.
Si no se especifica el mensaje de error, todos aquellos errores que se produzcan (o provoquemos) se
visualizarán en este punto.
@Html.ValidationMessage("Nombre")
Se visualizará de la siguiente manera
Por defecto asigna el estilo “field-validation-error”.
Action(s: [nombre acción])
Nos permite llamar a un método / acción de un controlador. Puede parecerse mucho a las vistas
parciales que vimos ayer y que recordaremos luego, pero lo cierto es que mientras las vistas parciales
están pensadas para escribir bloques de código, action está orientada a ejecutar el proceso completo
de la acción de un controlador (que incluye la generación del código con la Vista).
El resultado es una cadena de texto con todo el contenido generado.
@Html.Action("Cabecera")
Andrés Vallés Botella | Analista | Desarrollos propios
Servicio de Informática | Universidad de Alicante
Campus de Sant Vicent del Raspeig | 03690 | España
http://si.ua.es/es
10
ASP.NET MVC 3 Y 4 | DESARROLLO DE APLICACIONES CON EL FRAMEWORK MVC 3 Y 4
ActionLink(s: [descripción], s: [acción], s: [controlador])
Permite generar enlaces a acciones determinadas de un controlador. Por ejemplo si queremos poner
un enlace a la acción Index del Home usaríamos
@Html.ActionLink("Página principal", "Index", "Home")
Se usa en las plantillas para realizar cualquier acción con el modelo del controlador, alta, baja,
edición o borrado.
Dispone de muchas sobrecargas este Helper, permitiendo desde indicar protocolor, servidor y ancla,
hasta definir los atributos HTML.
RouteLink(s: [descripción], d: [valores ruta])
Es parecido alterior, porque genera un enlace a una ruta o una acción de un controlador. Es algo más
artesanal ya que no dispone de tantas sobrecargas y todos los valores se meten en un campo o se
llama a la routa por su nombre (en caso que se haya definido previamente). Si queremos enlazar con
la acción “Acerca de” usaríamos
@Html.RouteLink("Acerca de", new { controller = "Home", action="About"})
RenderAction(s: [nombre acción])
Es idéntica a Action con la diferencia de que no almacena el contenido en una cadena de texto si no
que la escribe directamente al objeto Response, con lo que se visualiza por el navegador.
Si deseamos que una acción sea sólo llamada desde Action o RenderAction pero no directamente
como una dirección más en el navegador podemos usar la anotación ChildActionOnly antes de la
declaración
[ChildActionOnly]
public ActionResult Cabecera() {
}
HELPERS CON EL MODELO
Lo normal no será generar todo el código HTML sino que se genere a partir de un modelo, por lo
tanto MVC ofrece tdo lo que hemos visto anteriormente para trabajar con modelos.
Analizamos un par, pero será lo mismo para el resto de compontes.
Partimos de que la vista incluye referencia al modelo por ejemplo al Libro que contiene una
propiedad título
@model Proyecto.Libro
Andrés Vallés Botella | Analista | Desarrollos propios
Servicio de Informática | Universidad de Alicante
Campus de Sant Vicent del Raspeig | 03690 | España
http://si.ua.es/es
11
ASP.NET MVC 3 Y 4 | DESARROLLO DE APLICACIONES CON EL FRAMEWORK MVC 3 Y 4
Todos los Helpers se llaman como antes pero postponiendo For al nombre, es decir TextBox pasa a
ser TextBoxFor o Label pasa a ser LabelFor. Luego para hacer referencia a una propiedad del modelo
se usa un alias por ejemplo m => m.propiedad o l => l.propiedad (lo que os sea más cómodo)
Si necesitamos mostrar la etiqueta, la caja de texto y la validación
@Html.LabelFor(l => l.Titulo)
@Html.TextBoxFor(l => l.Titulo)
@Html.ValidationMessageFor(l => l.Titulo)
Si lo ejecutamos lo primero que vemos es que el LabelFor de un modelo no es muy útil porque es el
nombre del campo. Muchas veces siglas de un campo de la base de datos, o todo en mayúsculas.
MVC incluye data annotations en los campos lo que permite personalizar la información que luego se
verá con estos Helpers. Por el momento sólo vamos a ver el de la descripción, pero mañana nos
centraremos en todo el tema de validación, que es donde realmente se saca el potencial.
Las anotaciones se ponen con corchetes antes de la definición de la propiedad.
[Display(Name = "Título del libro")]
public string Titulo { get; set; }
Display permite personalizar aspectos de la visualización. Con Name le indicamos el nombre o label
que tendrá esta propiedad. El resultado es:
PERSONALIZADOS
Como es lógico MVC nos permite crear nuestros propios Helpers para darle mayor potencia a éstos.
Se comportan como una función a la que se pasan parámetros si los necesita, y dentro genera el
código que queremos mostrar.
El formato es @Helper [Nombre Función]( [parámetros] )
@helper BreadCumb(string[] elementos) {
<div>
for(int i=0; i < elementos.Count(); i++)
{
<span>@elementos[i]</span>
if (i < elementos.Count() - 1)
{
<text>></text>
}
}
</div>
}
Luego para llamarla @NombreFunción(parámetros)
@BreadCumb(new[] {"Inicio", "Administración", "Secciones"})
Andrés Vallés Botella | Analista | Desarrollos propios
Servicio de Informática | Universidad de Alicante
Campus de Sant Vicent del Raspeig | 03690 | España
http://si.ua.es/es
12
ASP.NET MVC 3 Y 4 | DESARROLLO DE APLICACIONES CON EL FRAMEWORK MVC 3 Y 4
El resultado es el siguiente
Es costumbre a la hora de poner los parámetros, anteponer el nombre de cada parámetro y luego :
(dos puntos).
@BreadCumb(elementos: new[] {"Inicio", "Administración", "Secciones"})
Si deseamos que es helper sea reutilizables desde cualquier vista, añadimos la carpeta App_Code a
nuestro proyecto (no aparece como opción en las carpetas de ASP.NET) y creamos el fichero
BreadCumbHelpers.cshtml. Copiamos el código del Helper y lo guardamos.
Ahora para hacerle referencia desde la vista llamaremos al helper de la siguiente manera
@[Nombre fichero (sin extensión)].[Nombre del helper o función]( [parámetros])
@BreadcumbHelpers.BreadCumb(elementos: new[] { "Inicio", "Administración", "Secciones" })
Aunque puede parecer que crear un Helper es lo mismo que llamar a vistas parciales (que vimos
ayer) porque ambas se usan para reaprovechar código o dejarlo más estructurado, si que es cierto
que cada uno tiene su uso.
Helpers personalizado está pensado para pequeños trozos de código, que generan una programación
sencilla y que se comparte con diferentes vistas de tu proyecto o incluso entre varios.
Partial views están orientadas a secciones de código, con el objetivo de hacer más clara la estructura.
Puede contener una programación tan complicada como la vista que les llama.
Disponemos de dos HTMLHelpers dedicados a trabajar con Partial views (ayer usamos el comando
RenderPage).
Partial(s: nombrevista)
Genera una cadena de texto con la ejecución de la vista parcial
@Html.Partial("_Cabecera")
RenderPartial(s: nombrevista)
Es idéntica a Partial con la diferencia de que no almacena el contenido en una cadena de texto si no
que la escribe directamente al objeto Response, con lo que se visualiza por el navegador.
@{ Html.RenderPartial("_Cabecera"); }
PERSONALIZADOS CON HTMLHELPER
Andrés Vallés Botella | Analista | Desarrollos propios
Servicio de Informática | Universidad de Alicante
Campus de Sant Vicent del Raspeig | 03690 | España
http://si.ua.es/es
13
ASP.NET MVC 3 Y 4 | DESARROLLO DE APLICACIONES CON EL FRAMEWORK MVC 3 Y 4
Hay otra forma de crear Helpers personalizados en el que “construimos el contenido” que queremos
generar. Será una clase, con métodos que no generan HTML directamente como una vista parcial, si
no que devuelve un objeto de tipo IHtmlString. Las etiquetas se generan con TagBuildery los
atributos se añaden con MergeAttribute.
Un ejemplo muy útil, y que encontramos en muchas páginas, es crear nuestro propio ActionLink para
trabajar con imágenes.
using System.Web;
using System.Web.Mvc;
using System.Web.Mvc.Html;
using System.Web.Routing;
namespace _3_MVCHelpers.Helpers
{
public static class HtmlImageActionLinkHelper
{
public static IHtmlString ImageActionLink(
this HtmlHelper helper,
string imageUrl,
string actionName,
object routeValues,
object htmlAttributes
)
{
var builder = new TagBuilder("img");
builder.MergeAttribute("src", imageUrl);
builder.MergeAttributes(new RouteValueDictionary(htmlAttributes));
var link = helper.ActionLink("[replaceme]", actionName, routeValues);
var html = link.ToHtmlString().Replace("[replaceme]", builder.ToString(TagRenderMode.SelfClosing));
return new HtmlString(html);
}
}
}
Para referenciarlo en nuestra propia vista, usaremos @Html.[Método] ([Parámetros]).
Si queremos añadir una imagen 012.jpg que al pulsar sobre ella vaya a la acción Index usaríamos:
@Html.ImageActionLink(
Url.Content("~/Fotos/012.jpg"),
"Index",
new { id = 5 },
new { id = "imgnb", width = "100px", height = "150px", alt = "Foto playa de Alicante" }
)
LISTADOS
Uno de las interacciones más comunes con el usuario es mostrar listados. Puede ser un catálogo de
enlaces (por ejemplo en una tienda de libros, de música), un listado de preguntas (para poder
consultar las respuestas), resultado de buscar por algún termino, etc.
Por tanto hay que ser versátiles a la hora de mostrar los datos porque si no es operativa el usuario
dejará de usar nuestra aplicación.
PAGINACIÓN Y ORDENACIÓN
Los listados que hemos generado con las plantillas de ASP.NET MVC son muy básicos si lo
comparamos con los GridViews de los WebForms.
Andrés Vallés Botella | Analista | Desarrollos propios
Servicio de Informática | Universidad de Alicante
Campus de Sant Vicent del Raspeig | 03690 | España
http://si.ua.es/es
14
ASP.NET MVC 3 Y 4 | DESARROLLO DE APLICACIONES CON EL FRAMEWORK MVC 3 Y 4
El 5º día usaremos componentes externos que nos permite generar listados muy potentes, pero hoy
nos centramos en el helper WebGrid que está a medio camino entre uno y otro y que en la mayoría
de los casos nos sobrará.
Explicarlo con detalle es muy complicado, pero aplicarlo en ejemplo es muy sencillo. Vamos a crear
una nueva acción Grid que se consulte como el listado de Index
public ViewResult Grid()
{
return View(db.VCSI_LIBRO.ToList());
}
Ahora creamos la vista (podemos usar la plantilla de listado para ver los campos o vacía) y hacemos
una definición del Grid y luego lo mostramos.
@model IEnumerable<CSI_BibliotecaBDOracle.VCSI_LIBRO>
@{
ViewBag.Title = "Grid";
var grid = new WebGrid(
source: Model,
rowsPerPage: 4);
}
<h2>Grid</h2>
@grid.GetHtml(
columns: grid.Columns (
grid.Column("Isbn", "Isbn"),
grid.Column("Titulo", "Título"),
grid.Column("Descripcion_TipoLibro", "Tipo de libro")
)
)
El resultado en pantalla es
Lo primero que haremos será pulsar en la cabecera para ordenar. No obtendremos el resultado
deseado, pero si que veremos que va cambiando la URL Grid?sort=Titulo&sortdir=ASC,
sort=Titulo&sortdir=DESC&page=2, dependiendo de si pulsamos ordenar o paginar.
Seremos nosotros desde la acción los que gestionemos la ordenación, porque la paginación si que la
realiza el propio componente.
Actualizar la acción para mostrar los resultados es bastante sencill
public ViewResult Grid(string sort, string sortdir)
{
IEnumerable<VCSI_LIBRO> libros = db.VCSI_LIBRO;
if (!String.IsNullOrEmpty(sort))
Andrés Vallés Botella | Analista | Desarrollos propios
Servicio de Informática | Universidad de Alicante
Campus de Sant Vicent del Raspeig | 03690 | España
http://si.ua.es/es
15
ASP.NET MVC 3 Y 4 | DESARROLLO DE APLICACIONES CON EL FRAMEWORK MVC 3 Y 4
{
switch (sort.ToLower())
{
case "titulo":
libros = (sortdir == "DESC" ? libros.OrderByDescending(l => l.TITULO) : libros.OrderBy(l => l.TITULO));
break;
}
}
else
libros = libros.OrderBy(l => l.TITULO);
return View(libros.ToList());
}
Detectamos que nos llegue un campo por el que ordenar. Por cada campo miramos si la ordenación
es ascendente (por defecto) o descendente y actualizamos los libros a mostrar.
PERSONALIZACIÓN
Para comenzar con listados o grids el formato que nos ofrece es bastante atractivo. Si deseamos
modificar la apariencia debemos recurrir a los estilos CSS.
Por ejemplo WebGrid permite asignar el estilo a todos los elementos tableStyle, headerStyle,
rowStyle y footerStyle. Incluso permite alternar el estilo de cada fila con alternatingRowStyle.
Cuando veamos la plantilla de las aplicaciones de la UA, los listados se quedarán con el siguiente
formato
Andrés Vallés Botella | Analista | Desarrollos propios
Servicio de Informática | Universidad de Alicante
Campus de Sant Vicent del Raspeig | 03690 | España
http://si.ua.es/es
16
ASP.NET MVC 3 Y 4 | DESARROLLO DE APLICACIONES CON EL FRAMEWORK MVC 3 Y 4
GENERACIÓN
DE
VERSIONES
VALENCIANO E INGLÉS
IDIOMÁTICAS,
CASTELLANO,
CONFIGURAR Y DETECTAR EL IDIOMA POR DEFECTO
La forma más cómoda de gestionar el idioma es definir el método Application_AcquireRequestState
en Golbal.asax.cs. Almacenamos en la variable Session[“idioma”] el idioma por defecto. Luego la
asignamos a CurrentCulture del proceso actual. En cada llamada que hagamos se llamará a este
método.
protected void Application_AcquireRequestState(object sender, EventArgs e)
{
const string idiomaDefecto = "es";
if (HttpContext.Current.Session != null)
{
CultureInfo ci = (CultureInfo)this.Session["idioma"];
if (ci == null)
{
ci = new CultureInfo(idiomaDefecto);
this.Session["idioma"] = ci;
}
Thread.CurrentThread.CurrentUICulture = ci;
Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture(ci.Name);
}
else
{
CultureInfo ci = new CultureInfo(idiomaDefecto);
Thread.CurrentThread.CurrentUICulture = ci;
Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture(ci.Name);
}
}
RECURSOS
El elemento base para gestionar las traducciones son los recursos.
Una buena práctica es crearnos una carpeta Resources y meter todos aquellos ficheros que
necesitemos. Se pueden crear a nivel de controlador o modelo o a nivel de aplicación si nuestro
proyecto es muy básico.
Sobre el proyectos pulsamos botón derecho Agregar > Nueva carpeta y le ponemos el
nombre .
Ahora sobre esta nueva carpeta Agregar > Nuevo elemento … y seleccionamos (o filtramos) Archivo
de recursos. Le llamamos modelos.resx
Andrés Vallés Botella | Analista | Desarrollos propios
Servicio de Informática | Universidad de Alicante
Campus de Sant Vicent del Raspeig | 03690 | España
http://si.ua.es/es
17
ASP.NET MVC 3 Y 4 | DESARROLLO DE APLICACIONES CON EL FRAMEWORK MVC 3 Y 4
Añadimos las descripciones que queremos que estén en varios idiomas
Es importante que marquemos el fichero como público para que se pueda acceder desde el resto de
los elementos de MVC
Para crear la versión de recursos para otro idioma, copiamos y pegamos el que usemos de base y
luego le añadimo .[dos digitos del idioma] antes de la extensión del fichero.
Es nuestro caso para crear el fichero de recursos en inglés renombraríamos copia de modelos.resx
por modelo.en.resx y traduciríamos los valores de cada una de las etiquetas.
VISTAS
La manera más cómoda de pasar textos traducidos a las vistas, en caso de que sean pocos, es usar el
controlador y el objeto ViewBag. Para acceder a un recurso escribimos el [nombre carpeta de
recursos].[ nombre recurso (sin extensión].etiqueta.
ViewBag.Titulo = Resources.modelos.tituloLabel;
Luego en la propia vista lo referenciamos
Andrés Vallés Botella | Analista | Desarrollos propios
Servicio de Informática | Universidad de Alicante
Campus de Sant Vicent del Raspeig | 03690 | España
http://si.ua.es/es
18
ASP.NET MVC 3 Y 4 | DESARROLLO DE APLICACIONES CON EL FRAMEWORK MVC 3 Y 4
Texto del fichero de recursos: @ViewBag.Titulo<br />
Si la vista contiene muchos elementos a traducir, lo más sencillo es crear dos versiones de la vista de
la misma manera que lo hemos hecho con el fichero de recursos. Cogemos la vista base index.cshtml
la copiamos y la pegamos y renombramos por index.en.cshml. Hacemos los cambios que
corresponda
Creamos una clase para gestionar los idiomas CultureHelper. La variable Cultures almacena los
idiomas con los que trabajemos. El primero de ellos será el que se usará por defecto en caso de que
detectemos otro que no se corresponda con nuestro listado.
using System;
using System.Collections.Generic;
using System.Linq;
namespace _3_MvcGlobalization.Helpers
{
public static class CultureHelper
{
// Include ONLY cultures you are implementing as views
private static readonly Dictionary<String, bool> Cultures = new Dictionary<string, bool> {
{"es", true}, // first culture is the DEFAULT
{"en", true},
{"ca", true}
};
/// <summary>
/// Returns a valid culture name based on "name" parameter. If "name" is not valid, it returns the default culture "en-US"
/// </summary>
/// <param name="name">Culture's name (e.g. en-US)</param>
public static string GetValidCulture(string name)
{
if (string.IsNullOrEmpty(name))
return GetDefaultCulture(); // return Default culture
if (Cultures.ContainsKey(name))
return name;
// Find a close match. For example, if you have "en-US" defined and the user requests "en-GB",
// the function will return closes match that is "en-US" because at least the language is the same (ie English)
foreach (var c in Cultures.Keys)
if (c.StartsWith(name.Substring(0, 2)))
return c;
// else
return GetDefaultCulture(); // return Default culture as no match found
}
/// <summary>
/// Returns default culture name which is the first name decalared (e.g. en-US)
/// </summary>
/// <returns></returns>
public static string GetDefaultCulture()
{
return Cultures.Keys.ElementAt(0); // return Default culture
}
/// <summary>
/// Returns "true" if view is implemented separatley, and "false" if not.
Andrés Vallés Botella | Analista | Desarrollos propios
Servicio de Informática | Universidad de Alicante
Campus de Sant Vicent del Raspeig | 03690 | España
http://si.ua.es/es
19
ASP.NET MVC 3 Y 4 | DESARROLLO DE APLICACIONES CON EL FRAMEWORK MVC 3 Y 4
/// For example, if "es-CL" is true, then separate views must exist e.g. Index.es-cl.cshtml, About.es-cl.cshtml
/// </summary>
/// <param name="name">Culture's name</param>
/// <returns></returns>
public static bool IsViewSeparate(string name)
{
if (Cultures.ContainsKey(name))
return Cultures[name];
return false;
}
}
}
El siguiente paso es crear nuestro propio controlador que detectará el idioma y personalizará la vista.
Requerimos hacer uso de dos métodos de la clase Controller: ExecuteCore y OnActionExecuted.
El primero normaliza el idioma, lo almacena en una variable sesión y establece CurrentCulture.
En Session[“idioma”] vamos a almacenar el idioma que se haya detectado o el que se seleccione (lo
veremos luego). De esa manera lo podremos usar en cualquier vista o controlador.
El segundo establece la vista que debe abrir el controlador dependiendo del idioma que se haya
seleccionado.
using System.Globalization;
using System.Threading;
using System.Web.Mvc;
using _3_MvcGlobalization.Helpers;
namespace _3_MvcGlobalization.Controllers
{
public class UaController : Controller
{
protected override void OnActionExecuted(ActionExecutedContext filterContext)
{
// Detectamos si llamamos desde una vista
var view = filterContext.Result as ViewResultBase;
if (view == null) // En caso de que no sea, salims
return;
string cultureName = Thread.CurrentThread.CurrentCulture.Name;
// Buscamos si
if (cultureName == CultureHelper.GetDefaultCulture())
return;
// Are views implemented separately for this culture? if not exit
bool viewImplemented = CultureHelper.IsViewSeparate(cultureName);
if (viewImplemented == false)
return;
string viewName = view.ViewName;
int i;
if (string.IsNullOrEmpty(viewName))
viewName = filterContext.RouteData.Values["action"] + "." + cultureName; // Index.en-US
else if ((i = viewName.IndexOf('.')) > 0)
{
// contains . like "Index.cshtml"
viewName = viewName.Substring(0, i + 1) + cultureName + viewName.Substring(i);
}
else
viewName += "." + cultureName; // e.g. "Index" ==> "Index.en-Us"
view.ViewName = viewName;
Andrés Vallés Botella | Analista | Desarrollos propios
Servicio de Informática | Universidad de Alicante
Campus de Sant Vicent del Raspeig | 03690 | España
http://si.ua.es/es
20
ASP.NET MVC 3 Y 4 | DESARROLLO DE APLICACIONES CON EL FRAMEWORK MVC 3 Y 4
filterContext.Controller.ViewBag._culture = "." + cultureName;
base.OnActionExecuted(filterContext);
}
protected override void ExecuteCore()
{
var idioma = Session["idioma"];
string cultureName;
if (idioma != null)
cultureName = idioma.ToString();
else
{
cultureName = (Request.UserLanguages == null
? CultureHelper.GetDefaultCulture()
: Request.UserLanguages[0]);
if (cultureName.IndexOf("-") > 0)
cultureName = cultureName.Substring(0, cultureName.IndexOf("-"));
Session["idioma"] = cultureName;
}
// Normalizamos
var normalizedCultureName = CultureHelper.GetValidCulture(cultureName);
if (normalizedCultureName != cultureName)
{
cultureName = normalizedCultureName;
Session["idioma"] = cultureName;
}
// Actualizamos el idioma
Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture(cultureName);
Thread.CurrentThread.CurrentUICulture = CultureInfo.CreateSpecificCulture(cultureName);
base.ExecuteCore();
}
}
}
PLANTILLA
Por defecto la plantilla que se usa es la que se configure en /Views/ _ViewStart.cshtml.
Por seguir con este criterio, si modificamos este fichero y usamos la variable Session[“idioma”]
tendremos la plantilla que se debe usar.
@{
Layout = "~/Views/Shared/_Layout." + @Session["idioma"] + ".cshtml";
}
En clase lo optimizaremos para que no se produzcan errores cuando no esté definida esta variable.
CAMBIAR DE IDIOMA
Nuestra plantilla permite cambiar el icioma
public ActionResult ChangeLanguage(string language)
{
Session["idioma"] = language;
if (Request.UrlReferrer != null)
Andrés Vallés Botella | Analista | Desarrollos propios
Servicio de Informática | Universidad de Alicante
Campus de Sant Vicent del Raspeig | 03690 | España
http://si.ua.es/es
21
ASP.NET MVC 3 Y 4 | DESARROLLO DE APLICACIONES CON EL FRAMEWORK MVC 3 Y 4
return Redirect(Request.UrlReferrer.ToString());
return Redirect("Index");
}
[ @Html.ActionLink("English", "ChangeLanguage", "Home", new { language = "en" }, null) ]
[ @Html.ActionLink("Español", "ChangeLanguage", "Home", new { language = "es" }, null) ]
[ @Html.ActionLink("Valencià", "ChangeLanguage", "Home", new { language = "ca" }, null) ]
En caso de que nos envíen un idioma que no se corresponda con el listado admitido, no habrá
problemas porque en la siguiente llamada a Index (o la página desde donde se llamó) se detectará
que no se corresponde con uno de los admitidos y se asignará el que tengamos por defecto.
MODELO
Siguiendo el ejemplo del libro de antes, si queremos que la descripción del título salga del fichero de
recursos remplazamos
[Display(Name = "Título del libro")]
public string Titulo { get; set; }
por
[Display(Name = "tituloLabel", ResourceType = typeof(Resources.modelos))]
public string Titulo { get; set; }
En caso de que nos de un error de que no se encuentra un recurso público con ese nombre, recordar
lo de marcar Public en el fichero de recursos.
POCO
Cuando el modelo de los datos lo gestionamos desde EF se complica la posibilidad de incluir data
annotations porque no disponemos de forma sencilla el modelo y los atributos.
Lo vamos a hacer de forma manual para comprender el proceso. Creamos una clase partial donde
indiquemos los campos y podamos añadir las anotaciones.
public partial class CSI_LIBRO
{
....
}
Todos los campos se declaran de tipo virtual para no interferir con la declaración de EF
public partial class CSI_LIBRO
{
public virtual decimal ID
{
get;
set;
}
public virtual string ISBN
{
get;
set;
}
Ahora ya podemos incluir las anotaciones sin problemas.
Andrés Vallés Botella | Analista | Desarrollos propios
Servicio de Informática | Universidad de Alicante
Campus de Sant Vicent del Raspeig | 03690 | España
http://si.ua.es/es
22
ASP.NET MVC 3 Y 4 | DESARROLLO DE APLICACIONES CON EL FRAMEWORK MVC 3 Y 4
Generar código automático
El proceso anterior se puede gestionar cuando tenemos tablas básicas como con las que estamos
trabajando estas semanas, pero es inviable con una aplicación con varias tablas y con muchos
campos cada un de éstas.
Por eso EF incluye una opción de generar este código de forma automática. SI pulsamos el botón
derecho sobre una tabla veremos que disponemos de la opción “Agregar elemento de generación de
código”.
Nos abre una ventana que tiene seleccionado por defecto la plantilla de Datos. Seleccionamos
Código y nos saldrá una lista de plantillas. Seleccionamos EF 4.X POCO Entity Generator. Le ponemos
un nombre en la parte inferior, ModelBiblioteca.tt y pulsamos Agregar.
Volveremos al EF y aparentemente no veremos nada nuevo. Ha añadido una referencias y nos ha
creado el fichero ModelBiblioteca.Context.tt y ModelBiblioteca.tt.
Andrés Vallés Botella | Analista | Desarrollos propios
Servicio de Informática | Universidad de Alicante
Campus de Sant Vicent del Raspeig | 03690 | España
http://si.ua.es/es
23
ASP.NET MVC 3 Y 4 | DESARROLLO DE APLICACIONES CON EL FRAMEWORK MVC 3 Y 4
Si pulsamos sobre la flecha que hay a la izquierda de ModelBiblioteca.tt veremos que aparecen las
tablas y/o vistas que tengamos en nuestro EF.
Si abrimos una de estas clases veremos que es de tipo parcial y con todos los campos definidos como
virtual. Ahora le podemos añadir los atributos que veamos necesarios.
Si por ejemplo a los tipos de libros le incluímos:
[Required]
[Display(Name = "Descripción")]
public virtual string DESCRIPCION
Veremos los resultados cuando creemos o modifiquemos un tipo de libro. No nos permite dejarlo en
blanco y la decripción del campo pasa de DESCRIPCION a Descripción.
Aunque hemos visto que el proceso es muy sencillo y que se crea de forma muy rápida, debemos
tener mucho cuidado con las actualizaciones en la base de datos y en el modelo EF. Un cambio en el
EF implicará un cambio en el código generado con POCO. Lo más común y viendo su sencillez es
borrarlo y volverlo a crear. Todas nuestras anotaciones se perderán en este proceso.
Andrés Vallés Botella | Analista | Desarrollos propios
Servicio de Informática | Universidad de Alicante
Campus de Sant Vicent del Raspeig | 03690 | España
http://si.ua.es/es
24
ASP.NET MVC 3 Y 4 | DESARROLLO DE APLICACIONES CON EL FRAMEWORK MVC 3 Y 4
PROYECTO
CREAR TABLAS BÁSICAS, CONTROLADORES BÁSICOS Y MASTER PAGE
Andrés Vallés Botella | Analista | Desarrollos propios
Servicio de Informática | Universidad de Alicante
Campus de Sant Vicent del Raspeig | 03690 | España
http://si.ua.es/es
25
Descargar