lunes, 7 de junio de 2010
Las plantillas de edición, incorporadas con la versión 2 de ASP.NET MVC, permiten crear muy fácilmente interfaces de edición reutilizables, y asociarlas a tipos de datos concretos.
En el caso de las fechas se trata de una práctica muy habitual, puesto que la edición por defecto para este tipo de campos casi nunca es suficiente y siempre tendremos necesidad de modificar este comportamiento.
Es decir, pongamos una entidad del Modelo como la siguiente:
Si generamos desde Visual Studio la vista de creación o edición por defecto de esta entidad, obtendremos un código, más o menos como el siguiente:
(Por cierto, ya he hablado por aquí en otra ocasión sobre cómo generar el código correcto del andamiaje de edición para las entidades del Modelo con propiedades de tipo DateTime, decimal o double).
Ejecutando ahora la solución y accediendo a esta vista de edición podremos comprobar que el resultado está bastante lejos del que seguro desearíamos obtener. En particular, la fecha se introduce en un cuadro de texto bastante desarbolado y muy poco profesional:
Vamos a ver paso a paso cómo mejorar esto utilizando los templated helpers, una característica introducida en ASP.NET MVC 2 que nos brinda un mecanismo de creación de plantillas de visualización y edición de propiedades muy potente y reutilizable.
Concretamente utilizaremos el widget Datepicker, incluido en el framework jQuery UI, que además de permitir la introducción manual de fechas con el formato correcto, permite la selección visual a través de un bonito calendario.
El post es un poco largo, pues iremos muy despacio y comentando cada paso, pero una vez sepáis hacerlo no tardaréis más de cinco minutos en activar este editor para todos los controles de introducción de fechas de vuestro sitio web :-)
La página de downloads de jQuery UI permite personalizar el contenido del paquete a descargar. Por ejemplo, si únicamente vamos a utilizar el widget “DatePicker”, podríamos desmarcar el resto de componentes para aligerar el peso del paquete final.
Para pruebas, lo más sencillo es dejar marcados todos los elementos, así no nos dejamos por detrás nada; en producción sería mejor descargar una versión más ligera, sólo con los componentes que vayamos a utilizar.
También debemos elegir el tema visual a emplear. Esto es importante, pues determina la apariencia visual de los widgets empleados. En nuestro caso, seleccionaremos el tema “Start”, aunque hay una gran variedad de ellos a elegir, e incluso desde el mismo sitio web puedes personalizarlos y crear temas nuevos.
Una vez hecho esto, pulsamos el botón download y salvamos en nuestro equipo el archivo .zip generado. De él tendremos que extraer:
La captura de pantalla adjunta muestra cómo quedaría el proyecto MVC tras incluir estos elementos.
De esta forma estamos diciéndole al framework, poco más o menos: “eh, genera un cuadro de texto para editar la propiedad
Pero como en este caso lo que nos interesa no es utilizar un cuadro de texto sino un editor más potente, modificamos dicha línea, dejándola como sigue:
Y ahora la cosa ha cambiado; la sustitución de
Este editor específico se trata de una vista parcial, una plantilla que podemos implementar en un archivo .ascx a nuestro antojo, incluyendo todos aquellos elementos que consideremos interesantes a la hora de ayudar al usuario a editar dicha propiedad.
Existen diversas vías para indicar a ASP.NET MVC la plantilla de edición a utilizar en cada caso, aunque aquí utilizaremos la forma más potente: la selección basada en el tipo de datos, un mecanismo compartido por la tecnología de datos dinámicos (Dynamic Data), que simplifica enormemente la construcción de interfaces.
Su funcionamiento es muy simple:
Otra ventaja es que esta solución es aplicable incluso a toda clase de tipos de datos. Es decir, podemos crear plantillas como “Boolean.ascx” para crear editores para valores lógicos, o “Persona.ascx” si lo que queremos es definir la plantilla para la edición de la clase
Observad que dado que vamos a implementar un editor para la clase
Comentamos rápidamente lo que podemos ver ahí:
Como podéis observar, estamos utilizando el helper
Y lo mejor es que a partir de este momento todas las fechas que vayamos a editar en nuestra aplicación podrán beneficiarse de este editor simplemente introduciendo en las vistas la llamada al helper
Como se puede comprobar, se trata de una solución muy elegante, productiva, fácil de mantener y respetuosa con el patrón MVC, de la que seguro no podréis prescindir en cuanto la utilicéis y veáis lo sencillo que resulta crearos vuestra propia biblioteca de editores personalizados.
Ah, dejo el proyecto de demostración, para Visual Studio 2008, en mi Skydrive, por si queréis comprobar "en directo" lo bien que queda ;-)
Publicado en: Variable not found.
En el caso de las fechas se trata de una práctica muy habitual, puesto que la edición por defecto para este tipo de campos casi nunca es suficiente y siempre tendremos necesidad de modificar este comportamiento.
Es decir, pongamos una entidad del Modelo como la siguiente:
public class Persona
{
[Required(ErrorMessage="Dime el nombre, anda")]
public string Nombre { get; set; }
[Required(ErrorMessage="Hey, este dato es fundamental")]
[DisplayName("Fecha de nacimiento")]
public DateTime FechaNacimiento { get; set; }
}
Si generamos desde Visual Studio la vista de creación o edición por defecto de esta entidad, obtendremos un código, más o menos como el siguiente:
Ejecutando ahora la solución y accediendo a esta vista de edición podremos comprobar que el resultado está bastante lejos del que seguro desearíamos obtener. En particular, la fecha se introduce en un cuadro de texto bastante desarbolado y muy poco profesional:
Concretamente utilizaremos el widget Datepicker, incluido en el framework jQuery UI, que además de permitir la introducción manual de fechas con el formato correcto, permite la selección visual a través de un bonito calendario.
El post es un poco largo, pues iremos muy despacio y comentando cada paso, pero una vez sepáis hacerlo no tardaréis más de cinco minutos en activar este editor para todos los controles de introducción de fechas de vuestro sitio web :-)
1. Descargamos jQuery UI
El primer paso es hacernos con la infraestructura necesaria para implementar la solución, por lo que debemos descargar la librería jQuery UI. Aunque es un procedimiento sencillo, hay que tener cuidado con algunos detalles.Para pruebas, lo más sencillo es dejar marcados todos los elementos, así no nos dejamos por detrás nada; en producción sería mejor descargar una versión más ligera, sólo con los componentes que vayamos a utilizar.
También debemos elegir el tema visual a emplear. Esto es importante, pues determina la apariencia visual de los widgets empleados. En nuestro caso, seleccionaremos el tema “Start”, aunque hay una gran variedad de ellos a elegir, e incluso desde el mismo sitio web puedes personalizarlos y crear temas nuevos.
Una vez hecho esto, pulsamos el botón download y salvamos en nuestro equipo el archivo .zip generado. De él tendremos que extraer:
- por un lado, la hoja de estilos (.css) e imágenes utilizadas por el tema elegido, disponibles dentro de la carpeta
/css/start
del archivo .zip que hemos descargado. Descomprimimos esta carpeta sobre el directorio/Content
de nuestro proyecto MVC. - el archivo de scripts, jquery-ui-1.8.1.custom.min.js, disponible en la carpeta
/js
del fichero .zip, que descomprimimos sobre la el directorio /Scripts de nuestro proyecto.
La captura de pantalla adjunta muestra cómo quedaría el proyecto MVC tras incluir estos elementos.
2. Modificamos la vista
Si acudimos a la vista de edición de la entidad, observamos que la propiedad de tipo fecha se edita a través del control generado por el siguiente código:<%= Html.TextBoxFor(model => model.FechaNacimiento) %>
De esta forma estamos diciéndole al framework, poco más o menos: “eh, genera un cuadro de texto para editar la propiedad
FechaNacimiento
del modelo”, es decir, explícitamente le indicamos que la fecha de nacimiento sea editada con un text box.Pero como en este caso lo que nos interesa no es utilizar un cuadro de texto sino un editor más potente, modificamos dicha línea, dejándola como sigue:
<%= Html.EditorFor(model => model.FechaNacimiento) %>
Y ahora la cosa ha cambiado; la sustitución de
TextBoxFor
por EditorFor
viene a ser como hubiéramos cambiado la frase anterior por “eh, genera un editor específico para la propiedad FechaNacimiento
del Modelo”.Este editor específico se trata de una vista parcial, una plantilla que podemos implementar en un archivo .ascx a nuestro antojo, incluyendo todos aquellos elementos que consideremos interesantes a la hora de ayudar al usuario a editar dicha propiedad.
Existen diversas vías para indicar a ASP.NET MVC la plantilla de edición a utilizar en cada caso, aunque aquí utilizaremos la forma más potente: la selección basada en el tipo de datos, un mecanismo compartido por la tecnología de datos dinámicos (Dynamic Data), que simplifica enormemente la construcción de interfaces.
Su funcionamiento es muy simple:
- Durante la ejecución de la llamada al helper
Html.EditorFor()
, el framework examinará la propiedad que deseamos editar, determinando que se trata, en este caso, de un tipoDateTime
. - Si existe una plantilla llamada “DateTime.ascx” en un directorio definido por convención, renderizará su contenido como editor para la propiedad.
- En caso contrario, intentará generar un editor por defecto.
EditorFor()
, se utilizará la plantilla que hayamos creado para ello. De lo más DRY, vaya :-)Otra ventaja es que esta solución es aplicable incluso a toda clase de tipos de datos. Es decir, podemos crear plantillas como “Boolean.ascx” para crear editores para valores lógicos, o “Persona.ascx” si lo que queremos es definir la plantilla para la edición de la clase
Persona
del Modelo.3. Creamos la plantilla de edición
Al tratarse de componentes de la Vista, las plantillas de edición de un tipo concreto las crearemos dentro de la carpeta /Views del proyecto, y más concretamente en:/Views/{ControladorActual}/EditorTemplates
, si lo que queremos es que la plantilla sea utilizada exclusivamente dentro del contexto de un controlador./Views/Shared/EditorTemplates
, lo más frecuente, si queremos que la plantilla de edición sea utilizada desde cualquier punto del sistema.
Observad que dado que vamos a implementar un editor para la clase
DateTime
, estamos creando una vista parcial fuertemente tipada hacia dicho tipo. El código para la plantilla DateTime.ascx
es el siguiente:- La vista parcial hereda de
ViewUserControl<DateTime>
, pues es el tipo de datos sobre el que va a operar. La propiedadModel
a nivel de vista será por tanto una fecha. - Lo primero que hacemos es generar, ahora sí, un cuadro de edición para editar el valor suministrado. Aquí destacan varios detalles:
- en primer lugar, el nombre o identificador de la propiedad está como cadena vacía. Esto es así porque es la vista que utiliza esta plantilla la que le suministrará el nombre; tened en cuenta que el mismo editor podría ser utilizado para varias propiedades, por lo que no tendría sentido introducir aquí el nombre de ninguna de ellas.
- A continuación asignamos un valor inicial al control, que obtenemos formateando la fecha.
- Por último, añadimos varios atributos HTML al tag <input>, como el tamaño máximo del texto o la clase CSS que queremos utilizar.
- Incluimos también en la vista un texto fijo indicando el formato de la fecha (dd/mm/aaaa).
- Finalmente, creamos el script que hace la magia. Detectamos si están presentes tanto jQuery como Datepicker y, en caso afirmativo, inicializamos el plugin para que tome el control del cuadro de edición que acabamos de generar.
4. Incluimos las referencias a las librerías de scripts y estilos
Por último, sólo nos falta añadir las referencias a las librerías y estilos en el punto donde nos interese:- si vamos a utilizar el editor u otras características de jQuery, o UI por toda la aplicación, lo más razonable es incluirlas en la página maestra del sitio,
- en caso contrario, si se trata de un uso puntual, podemos añadirlas exclusivamente a la vista donde se necesite.
<script src="<%= Url.Content("~/Scripts/jquery-1.4.1.min.js")%>"
type="text/javascript"></script>
<script src="<%= Url.Content("~/Scripts/jquery-ui-1.8.1.custom.min.js")%>"
type="text/javascript"></script>
<script src="<%= Url.Content("~/Scripts/jquery.ui.datepicker-es.js")%>"
type="text/javascript"></script>
<link href="<%= Url.Content("~/Content/start/jquery-ui-1.8.1.custom.css")%>"
rel="stylesheet" type="text/css" />
Como podéis observar, estamos utilizando el helper
Url.Content
para obtener la ruta absoluta de los recursos.5. Y… ¡voilà!
¡Hemos terminado! Si ejecutamos ahora nuestra aplicación, podremos comprobar que tanto el aspecto como la usabilidad del resultado han mejorado notablemente:Html.EditorFor()
:-). Como se puede comprobar, se trata de una solución muy elegante, productiva, fácil de mantener y respetuosa con el patrón MVC, de la que seguro no podréis prescindir en cuanto la utilicéis y veáis lo sencillo que resulta crearos vuestra propia biblioteca de editores personalizados.
Ah, dejo el proyecto de demostración, para Visual Studio 2008, en mi Skydrive, por si queréis comprobar "en directo" lo bien que queda ;-)
Publicado en: Variable not found.
16 Comentarios:
Sigo tu blog siempre , y da la casualidad que yo tambien ando liado con el MVC 2.0 en un proyecto y parece que me espies por que vas poniendo las cosas poco despues que yo las "descubra"/"haga" je je , yo que despues iba a hacer post en mi blog de como hacer las cosas... ahora seria "plagio".
Aqui cap:
http://img707.imageshack.us/img707/8984/1275987573010.png
Saludos y muchas gracias por cada uno de tus articulos , todos son muy utiles.
Hola!
Mira a tu espalda, puede que tengas una de mis webcams espía detrás :-DDD
Bueno, me alegro de que te sean útiles, aunque lleguen un poco tarde ;-D
Saludos & gracias por comentar.
Buenas!! Que tal? Mi pregunat es como se haria con mvc3 razor? Lo estoy intentando pero no logro hacerlo funcionar.. sabes como es? Un Saludo y gracias por compartir tus conocimientos.
Hola, Salvador!
Pues en Razor es prácticamente igual.
Simplemente nombra el editor personalizado como DateTime.cshtml, sustituye la directiva de encabezado por @model DateTime, y cambia el bloque <%= Html.blablabla %> por @Html.blablabla.
No debería darte muchos problemas.
Saludos.
Perfecto!! He realizado lo que has comentado y voila!!! 100% operativo. Gracias por la ayuda!!
Primero que felicitaciones por el articulo. Una consulta es donde especificamos en el modelo para que cuando haga el binding de la propiedad, utilice el formato ddmmyyyy. Para que no vaya a intepretarlo distinto (mmddyyyy).
Gracias.
Hola, @Tato.
El binder debe utilizar la cultura activa en el hilo actual, por lo si la tienes bien establecida (desde código o desde el web.config) lo tienes solucionado.
Un saludo!
Hola, José!
Gracias por publicar este articulo me ha servido de mucho, he realizado el mismo ejemplo con Razor y VB.
Hola muchas gracia jose, estoy haciéndolo en Razor tal como indicaste en uno de tus comentarios y me aparece el siguiente error
"El elemento de modelo pasado al diccionario es NULL, pero este diccionario requiere un elemento de modelo distinto de NULL de tipo 'System.DateTime'."
Hola, Cadavid.
El error indica que estás pasando un valor nulo a la vista parcial, y ésta espera un valor no nulo (básicamente porque DateTime es no soporta nulos!).
Si cambias el @model por DateTime? ya la vista soportará también valores nulos, aunque deberás realizar comprobaciones con el valor cuando se utilice en algún punto (por ejemplo, para establecer el valor inicial del textbox).
Saludos.
en primera muuuchas gracias por tu aporte me sirvio de mucho, solo el detalle del nombre de las librerias ya que haces mencion a ellas como 1.8.1 y resulta que los que baje eran 1.8.21 pero fuera de ese pequeño detalle todo bien, quiza sea por la fecha en que yo lo empeze a manejar,..
Hola Jose, muy bueno el aporte.
Cdo decis: "Si cambias el @model por DateTime? ya la vista soportará también valores nulos" Te referia poner: @model DateTime?.
Al ponerlo asi me da el siguiente msj: TutorialMVC1\TutorialMVC1\Views\Shared\EditorTemplates\DateTime.cshtml(4): error CS1061: 'System.Nullable' no contiene una definición de 'ToShortDateString' ni se encontró ningún método de extensión 'ToShortDateString' que acepte un primer argumento de tipo 'System.Nullable'
Slds
Hola, Fredy!
Claro, al usar el tipo "DateTime?" debes tener en cuenta que ya se trata de un tipo anulable, y tratarlo como tal.
Esto es, una vez hayas comprobado que el valor no es nulo, podrías llamar a @Model.Value.ToShortDateString().
Saludos.
Gracias Jose, un abrazo
Hola José.
Gracias por el aporte.
Estoy intentando utilizar este calendario con el modelo que tiene un combo para elegir año y mes.
En el código veo que implementa la línea.
input type="text" id="datepicker"
como lo implemento con:
@Html.EditorFor(model => model.FechaNacimiento)
@Html.ValidationMessageFor(model => model.FechaNacimiento)
gracias desde ya
Buenas,
Con este DatePicker no se muestran calendarios distintos al gregoriano, con lo que no me parece válido para una auténtica globalización. Aparte de este punto, me parece que está genial. Un saludo!
Enviar un nuevo comentario