ASP.NET MVC Framework (4ª Parte): Escenarios de edición y publicación

En estas semanas he estado trabajando en una serie de post sobre ASP.NET MVC. Es una solución que podemos usar para estructurar las aplicaciones web ASP.NET para separar conceptos, y hacer más sencillos los test unitarios de nuestro código y permitir un desarrollo dirigido por test (TDD, Test Driven Development).

En el primer post creamos un sitio simple de e-comerce para navegar entre los diferentes producots. Vimos algunos de los conceptos de alto nivel que hay tras MVC, y vimos cómo crear un proyecto ASP.NET MVC desde cero hasta implementar y testear esta funcionalidad. En el segundo post nos metimos más a fondo en la arquitectura de rutado de ASP.NET MVCy hablamos sobre cómo funionab y cómo podemos manejar escenarios de rutado de urls más avanzados. En el tercer post vimos cómo interactúan los controladores con las vistas, y vimos las diferentes formas que hay de pasar datos de un controlador a una vista para renderizar la respuesta.

En el post de hoy veremos algunas formas de manejar las entradas y salidas con el framework MVC, y hablaremos sobre algunos métodos de extensión de HTML que podemos usar para hacer que los escenarios de edición de datos sean más fáciles. Aquí tenéis el código de la aplicación completa que vamos a crear para explicar estos conceptos.

Escenarios

Para comprender lo básico de administrar los escenarios de input y posting con ASP.NET MVC, vamos a crear un escenario de creación, modificación y listado de productos. Habrá tres experiencias de usuario:

Listar productos por categoría

Los usuarios podrán ver todos los productos de una categoría navegando a la url /Products/Category/[CategoryID]:

Añadir un nuevo producto

Los usuarios serán capaces de añadir productos haciendo clic en el link "Add New Product". Esto les lleva a la url /Products/New - entonces se le pedirá que introduzca los datos necesarios para añadir un producto:

Cuando le den al botón Save, el producto se añadirá a la base de datos y se le redireccionará a la página de listado.

Edición de un producto

En la página de listado, los usuarios pueden hacer clic en el link "Edit" en cada producto. Esto les redirecciona a una url del tipo /Products/Edit/[ProductID] en la que podrán cambiar cualquier detalle y guardarlo en la base de datos cuando le den al botón Save:

Nuestro modelo de datos

Vamos a usar la base de datos de ejemplo Northwind para guardar nuestors datos. usaremos LINQ to SQL para modelar los objetos Products, Category y Supplier que representan las filas en nuestras tablas.

Hacemos clic derecho en el directorio /Models y hacemos clic en "Add New Item" -> "LINQ to SQL Classes" y veremos el diseñador de LINQ to SQL:

Crearemos una clase parcial NorthwindDataContext y añaderemos algunos métodos de ayuda. Definiremos estos métodos por dos razones: 1) nos evita tener que meter las consultas LINQ en las clases Controlladores y 2) nos permite adaptar más fácilmente nuestro controlador  para que use dependency injection más adelante.

Los métodos que añadiremos a NorthwindDataContext son:

Para aprender más sobre LINQ y LINQ to SQL echadle un vistado al tutorial.

Creando la clase ProductsController

Vamos a implementar el núcleo para las tres experiencia de navegación con una sola clase controlador - que llamaremos "ProductsController" (clic derecho en el direcotrio "Controllers" y le damos a "Add New Item"->"MVC Controller" para crearla:

La clase ProductsController trabajara con urls del tipo /Products/Category/3, /Products/New y /Products/Edit/5 para implementar las acciones "Category", "new" y "Edit":

Leed los dos primeros post de esta serie para aprender más sobre cómo se rutan estas urls a los métodos de acción de los controladores. Para este ejemplo usaremos la regla por defecto /[Controller]/[Action]/[Id] - con lo que no tendremos que configurar nada para que ocurra el rutado.

Las acciones del controlador usarán tres páginas de Vistas para renderizar la salida. Las páginas "List.aspx", "New.aspx" y "Edit.aspx" estarán en el directorio ViewsProducts, y se basarán en la master Site.Master que está en el directorio ViewsShared.

Implementando el listado por categorías

La primera parte que implementaremos será el listado de productos (/Products/Category/[CategoryId]):

Implementaremos esta funcionalidad usando la acción Category de la clase ProductsController. Usaremos la clase de LINQ to SQL DataContext, y el método GetCategoryById que añadimos, para obtener un objeto Category que representa la categoría que se indica en la url (por ejemplo: /Produtcs/Category/3). Pasaremos el objeto Category a la vista "List" para que renderize la respuesta:

 Cuando implementemos esta vista primero actualizaremos el code-behind para que derive de ViewPage<Category> de manera que la propiedad ViewData sea del tipo Category que le pasamos al controlador (en el tercer post de la serie se ve esto en más detalle):

Ahora implementamos el List.aspx de la siguiente forma:

Este aspx renderiza el nombre de la categoría al principio de la página, y luego renderiza una lista de productos de esa categoría.

Al lado de cada producto hay un enlace "Edit". Usaremos el método de acción Html.ActionLink que vimos en la segunda parte para renderizar un hyperlink HTML (por ejemplo: <a href="/Products/Edit/4" mce_href="/Products/Edit/4">Edit</a> que cuando lo pulsemos hará que el usuario navegue a la acción "Edit". También usaremos el método ActionLink para que en la parte de arriba aparezca el link <a href="/Products/New" mce_href="/Products/New">Add New Product </a> que actuará en consecuencia.

Cuando vamos a la url /Products/Category/1 y le damos a ver el código fuente desde el navegador, veremos que MVC a emitido un código HTML muy limpio:

Implementación de añadir producto (Primera parte)

Vamos a implementar la funcionalidad para añadir un producto. Querremos que los usuarios vean una pantalla como la siguiente cuando vayan a la url /Products/New:

En la entrada y edicion de datos ASP.NET MVC se basa en dos métodos de acción de las clases controladoras. El primero se encqarga de mandar el HTML inicial para mostrar. El segundo administra cualquier misión secundaria que se mande desde el navegador.

Por ejemplo, para la pantalla anterior podríamos implementarlo a través de dos acciones diferentes de ProductsController, uno llamado "New" y otro llamado "Create". La url /Products/New sería responsable de mostrar un form vacío con cajas de texto HTML y listas desplegales para introducir los detalles del nuevo producto. El elemento HTML <form> tendrá un atributo "action" en la url /Products/Create. Con esto, cuando un usuario haga clic en el boton de submit, las entradas del formulario se enviará a la acción Create y actualizará la base de datos:

Implementación de añadir producto (Segunda parte)

Aquí tenéis una primera implementación que podríamos usar para el ProductsController:

Notad que ahora tenemos dos métodos de acción involucrados en el proceso de creación de productos - "New" y "Create". El método "New" sólo muestra un formulario vacío al usuario. El método "Create" ees el que procesa los valores que hay en el formulario, crea el nuevo producto en la base de datos y redirige al usuario a la página de listado de categorías del producto.

El HTML enviado al cliente está implementado en la vista "New.aspx" llamada por el método de acción "New". Una primera implementación de esto (usando cajas de texto para todo) podría ser este:

Fijaos cómo usamos un elemento HTML stándar <form> en la página (no un form runat=server). El atributo "action" del form apunta al método de acción "Create" de la clase ProductsController. Habrá un postback cuando se pulse el boton <input type=submit value=save/>. Cuando esto ocurra, ASP.NET MVC mapeara el ProductNam, CategoryID, SupplierId y UnitPrice como parámetros del método "Create":

Y ahora cuando ejecutemos nuestro sitio, la funcionalidad básica funciona:

Implementación de añadir producto (Tercera parte - con listas desplegables)

La ventana de entrada de productos que hemos creado funciona, pero no es muy agradable. Es necesario que el usuario sepa los números CategoryID y SupplierID. Arreglaremos esto con una lista desplegable HTML con los nombres de las categorías y de los suministradores.

El primer paso es modificar la clase ProductsController para que le pase a la vista dos colecciones - una que contenga la lista de las categorías, y otra con los suministradores. Haremos esto creando una clase fuertemente tipada ProductsNewViewData que los encapsule, y que le pasaremos a la vista (esta parte la explicamos a fondo en el tercer post de la serie):

Ahora actualizamos el método de acción New para que obtenga estas colecciones y las pase al ViewData para la vista New:

Ahora podemos usar estas colecciones en la vista para que genere las listas desplegables HTML <select>

ASP.NET MVC HTML Helpers

Una solución que podemos usar es generar los desplegables manualmente con el código inline <%%> con un bucle que contenga sentencias if/else en el HTML. Esto nos da un control total sobre el HTML -pero haría el HTML desordenado.

Una forma más clara es ayudarnos de la propiedad "Html" en la clase base ViewPage. Este es un objeto que expone un conjunto de métodos de ayuda HTML que generan componentes HTML. Por ejemplo, anteriormente usamos el método Html.ActionLink para generar elementos <a href="">

El objeto HtmlHelper (así como el objeto AjaxHelper - del que hablaremos en otro post) ha sido diseñado para ser fácilmente extendido usando métodos de extensión. De manera que cualquiera puede crear sus propios métodos de ayuda para estos objetos.

Tenemos docenas de métodos HTML y AJAX para integrarlos en futuras versiones de ASP.NET MVC. En la primera versión sólo el método ActionLink está en System.Web.Extensions (en este assembly está el núcleo de ASP.NET MVC). También tenemos una MVCToolkit a parte para descargar que podeis añadir a vuestro proyecto para tener docenas de métodos auxiliares.

Para instalar los métodos de MVCToolkit, sólo añadid la dll MVCToolkit como referencia:

Recompilad el proyecto. Y la próxima vez que escribais <%=Html. %> vereis muchos más métodos:

Para crear las listas <select> podéis usar el método Html.Select(). Cada método viene con varias sobrecargas - todas con intellisense:

Podemos actualizar la vista "New" para que use las opciones de Html.Select y que muestre los desplegables que usará las propiedades CategoryId/SupplierID como valores a usar y que muestre los nombres:

Esto generará el HTML apropiado en ejecución:

Y les da a los usuarios una forma fácil de seleccionar la categoría y el suministrador:

Implementación de añadir producto (Cuarta parte - con el método UpdateFrom)

El método de acción "Create" de ProductsController es el responable de administrar los postback del escenario de añadir productos. Usa los parámetros como argumentos para el método de acción:

Esta forma funciona bien - pero para formularios con muchos datos y parámetros los métodos de acción pueden ser un poco pesados de leer. El código anterior que pone todos los valores de los parametros para el nuevo producto es ya un poco largo.

Si referenciamos el assembly MVCToolkit, podemos usar un método de extensión que hay en System.Web.Mvc.BindingHelpers que nos puede ayudar a limpiarlo un poco. Se llama "UpdateFrom" y se puede usar en cualquier objeto .NET. Toma un diccionario de valores como argumento y hace una asiganación automática de propiedades para cualquier clave que tenga una propiedad pública.

Por ejemplo, podemos reescribir el método Create para que use UpdateFrom:

Nota: Si queréis ser más explícitos por temas de seguridad y que sólo algunas propiedades puedan ser actualizadas, podeis parar un array de strings con los nombres de las propiedades para actualizar el método UpdateFrom:

Implementando la edición de productos (Primera Parte)

Ahora vamos a implementar la funcionalidad de edición de productos. Querremos que los usuarios vean una pantalla como la siguiente cuando visiten una url del tipo /Products/Edit/[ProductId]:

Como en el formulario de añadir producto, vamos a implementar esto usando dos acciones de ProductsController que llmaremos "Edit" y "Update":

"Edit" mostrará los datos del producto: "Update" será usado para modificarlo.

Implementando la edición de productos (Segunda parte - Accion Edit)

Empezaremos habilitando la funcionalidad de edición implementando el método de acción Edit en ProductController. Cuando creamos la página de listado al principio, lo hicimos de forma que la ación edicion tomará el argumento de la URL (Por ejemplo: /Products/Edit/5):

Queremos que el método de acción obtenga los datos del producto correcto de la base de datos, y las colecciones necesarias para los suministradores y las categorías (para que podamos poner unos desplegables en la vista de edición). Definiremos un tipo fuertemente tipado para que represente todo esto en ProductsEditViewData:

Podemos implemnetar el método de acción edit para que obtenga los datos del objeto ViewData y renderize la vista "Edit":

Implementando la edición de productos (Segunda parte - Vista Edit)

Podemos implementar la vista Edit.aspx de la siguiente forma:

Fijáos cómo usamos los métodos Html.TextBox y Html.Select. Ambos están en el assembly MVCToolkit.

FIjáos´que el método Html.Select tiene una sobrecarga que nos permite especificar cual es el valor para seleccionar en el desplegable. En el siguiente código le estamos diciendo que queremos que la categoría del desplegable que se seleccionará por defecto sea la de la categoría actual del producto:

Por último, fijáos cómo estamos usando el método Url.Action() para poner los elementos <form> :

Los métodos Url.Action y Html.ActionLink usan el motor de rutado de MVC para generar las urls (en la segunda parte de la serie vimos cómo funciona). Con esto, si cambiamos las reglas de rutado para la edición, no tendremos que cambiar ningún código del controlador o de la vista. Por ejemplo, podríamos remapear las urls de la forma /Products/1/Edit, y todo seguría funcionando igual.

Implementando la edición de productos (Tercera parte - Actualizar)

El último paso es implementar el método de acción "Update" de la clase ProductController:

Como en el método "Create" usaremos el método de extensión "UpdateFrom" para obtener los datos del producto de la petición. Fijáos que en vez de obtener un producto vacío, estamos usando un patron donde el primero obtiene los valores antiguos de la base de datos, le aplica los cambios y los guarda en la base de datos.

Una vez que se ha hecho la edición, volvemos a la página de listado de productos - y nos vamos a la categoría del producto modificado /products/Category/[CategoryID].

Resumen

Hemos visto algunos detalles más sobre cómo implentaríamos los escenarios de modificar y crear objetos con el ASP.NET MVC Framework, y hemos visto como podemos usar y estructurar escenarios de edición e inserción de datos.

Aquí tenéis un archivo zip con todo el código fuente del ejemplo que hemos creado.

En proximos post veremos cómo usar la validación y la recuperación de errores en escenarios de entrada y edición de datos. Hablarémos sobre algunos temas sobre seguridad para permitir la creación de apliaciones rápidamente. Veremos cómo usar ASP.NET AJAX para hacer sitios de edición con AJAX y MVC. Y veremos más a fondo cómo podemos crear test unitarios en nuestro controladores.

Espero que sirva.

Scott.

Traducido por: Juan María Laó Ramos. Microsoft Student Partner

Artículo original: http://weblogs.asp.net/scottgu/archive/2007/12/09/asp-net-mvc-framework-part-4-handling-form-edit-and-post-scenarios.aspx

Un pensamiento en “ASP.NET MVC Framework (4ª Parte): Escenarios de edición y publicación

Deja un comentario