2.4 Decision Process on Source to Buy and How to Buy
2.4.4 Determining vendor support during buying process
recapitulemos lo que hemos hecho hasta ahora: hemos usado
establecemos el estilo de los elementos seleccionados con la propiedad
• se-
lectedItemClass del dataView.
empleamos
• sys:key para darle un nombre accesible al dataview de productos
que actuará de maestro en esta visualización maestro-detalle.
De acuerdo. ahora que ya sabemos seleccionar visualmente un elemento de las plantillas, veamos cómo podemos enlazarlo con otra plantilla que visualice los con- tenidos correspondientes al detalle.
en este caso para visualizar los detalles usaremos un conjunto de etiquetas y controles de texto que además nos permitirán editar los campos de datos. Para simplificar el ejemplo sólo mostraremos los campos correspondientes al nombre del producto y a su precio.
el código necesario para obtener esta vista es el que se muestra en la figura 5.
Figura 5.- código de la vista de detalle
estamos usando etiquetas estándar de hTML 4.0 (<fieldset>, <legend>, <label>, etc...), pero podríamos haber usado otros elementos cualquiera según nuestras nece- sidades. se trata de una plantilla normal y corriente.
Lo único importante aquí es que nos fijemos en cómo estamos asociando los datos a visualizar por el control dataView de la plantilla. hemos usado una sintaxis especial de las etiquetas {} de Live binding:
dataview:data=”{binding selectedData, source={{TablaProductos}} }”
esta expresión hace que, como datos para visualizar en la plantilla, se utilice la propiedad selectedData (o sea, una referencia al elemento seleccionado) en el control llamado “TablaProductos”, que es el primer dataView.
¡Listo! si ahora ejecutas la página verás que al pulsar en cualquier producto, éste se selecciona y se visualizan su nombre y su precio en los campos de la segunda plantilla de detalles, a la derecha (figura 6).
Figura 6.- vista maestro-detalle de nuestros datos.
Lo más interesante de la vista de detalles que hemos hecho es que nos permite editar los datos. si seleccionas cualquier elemento y lo editas en su vista de deta- lles, podrás comprobar que al modificar el título o el precio, el cambio se refleja inmediatamente en la tabla de productos usando Live Binding. ¡Para conseguir esto no hemos tenido que escribir ningún tipo de código!.
11.- dEvolvEr loS dAToS ModiFicAdoS Al
SErvidor: coNTEXToS dE dAToS
si juegas un poco con el ejemplo anterior verás que modificas los datos pero en cuanto cargas la página de nuevo todos los cambios se pierden. esto es normal ya que el código Javascript está atado a la vida de cada página, y además no hemos definido ninguna forma de enviar los datos modificados al servidor.
sería perfecto tener un botón disponible en algún lado de la página que, tras haber editado tantos registros como queramos, podamos pulsar para enviar los cambios al servidor y persistirlos en la base de datos. esto cerraría el círculo de nuestra visión de un desarrollo aJaX “puro”. como veremos es muy fácil de conseguir gracias a asP.NeT aJaX.
Dentro del espacio de nombres Sys.Data existe una clase especial orientada a trabajar con datos del servidor desde el lado cliente: DataContext.
esta clase permite encapsular toda la funcionalidad necesaria para comunicarse con el servidor, para obtener datos y también para devolverlos. además, tiene la capacidad de llevar un registro de todos los cambios efectuados en los datos ori-
Nota:
si no te funciona la actualización en la tabla principal asegúrate de que has usado en ésta la sintaxis {binding NombreCampo} para enlazar los datos, y no las dobles llaves que, como sa- bemos, no habilitan el Live binding.
ginales traídos desde el servidor. Podemos saber cuáles se han modificado, cuáles son nuevos o qué datos se han borrado. Lo más impresionante es que un sólo objeto
DataContext es capaz de llevar el control de cambios de todos los datos obtenidos
del servidor, aunque éstos hayan sido obtenidos de métodos diferentes y devuelvan objetos de diferentes clases.
Posteriormente sólo con una llamada a su método saveChanges(), todos los cam- bios que haya habido en el cliente se envían de una sola vez al servidor para ser persistidos. ello convierte la operación en un juego de niños.
existe una versión especializada de esta clase orientada para trabajar con aDo. NeT services en lugar de con servicios Web: AdoNetDataContext.
sabiendo esto vamos a rematar nuestro ejemplo añadiéndole la capacidad de enviar al servidor todas las modificaciones que hagamos en los registros. en este caso no le vamos a añadir la capacidad de borrar y añadir registros, pero sería algo muy sencillo y se deja como ejercicio para el lector.
supongamos que tenemos un método ya creado en nuestro servicio que se llama
SaveProducts (suelo usar siempre todos los nombres de métodos públicos en inglés),
y que sirve para guardar los cambios enviados desde el cliente. Luego volveremos sobre él para explicar la forma de crearlo porque es lo más difícil, pero de momento vamos a dar por sentado que ya existe.
Lo primero que tenemos que hacer es utilizar como origen de datos para nuestras plantillas un objeto DataContext, ya que éste se encargará de traer la información y luego mantener un registro de cambios producidos sobre ella.
Lo podemos declarar instanciando por código un objeto Sys.Data.DataContext y luego ir estableciendo sus propiedades a mano. sin embargo es más robusto e integrado usar una llamada al método especial $create dentro del evento de ini- cialización de la aplicación, como ya vimos en el apartado 4. Por ello añadiremos este código a la página (en el ejemplo descargable se encuentra dentro del archivo auxiliar “apoyoBinding.js”): var dc; Sys.Application.add_init(function() { dc = $create(Sys.Data.DataContext, { serviceUri: “Servicios/NorthwindService.svc”, saveOperation: “SaveProducts” }); });
Lo único que hemos hecho es crear el contexto de datos y decirle cuál es la ruta del servicio que debe utilizar para sus operaciones, así como el nombre de la operación de guardado de datos (propiedad saveOperation), que en este caso se llama SaveProducts como ya hemos comentado. Dejamos una variable global llamada dc que nos servirá para tener acceso a este contexto de datos y así poder utilizarlo desde el código.
el otro cambio que debemos hacer es indicar a nuestro dataView —encargado de mostrar los datos de los productos— que debe usar ese contexto como proveedor de datos. hasta el momento le habíamos asignado directamente la ruta del servicio, pero ahora le vamos a decir que use nuestro contexto de datos para obtener la información:
<tbody id=”Productos-template” class=”sys-template” sys:attach=”dataview” dataview:httpverb=”GET” dataview:autofetch=”true” dataview:dataprovider=”{{ dc }}” dataview:fetchoperation=”GetAllProducts” dataview:selecteditemclass=”seleccionado” dataview:sys-key=”TablaProductos”>
Fíjate como en la propiedad dataProvider le hemos asignado nuestro recién crea- do contexto de datos. si ahora ejecutas la página veremos que, en apariencia, nada ha cambiado en su funcionamiento. sin embargo, lo que no se percibe es que ahora todos los cambios que hacemos en los datos quedan registrados por el DataContext, lo que sí es un cambio significativo aunque no se manifieste visiblemente.
Gracias a esto lo único que nos queda es enviar los resultados al servidor. co- locaremos un botón en la página, y como único código necesario para su evento
click escribiremos:
dc.saveChanges();
¡Listo! esto enviará los datos que se hayan modificado al servicio web, el cual deberá encargarse de almacenar los cambios en la base de datos. Más sencillo imposible.
12.- lA dEFiNicióN dEl MéTodo dE guArdAdo
dE cAMbioS
No existe documentación sobre cómo se debe crear un método para un servicio Web que se use para recibir la información de datos modificados desde un dataContext de lado cliente. el motivo es que todos los ejemplos y documentación que puedes en- contrar por Internet usan la clase AdoNetDataContext y un servicio autogenerado de
Nota:
En el ejemplo de código que puedes descargar de la web de Krasis Press he incluido un ejemplo más completo en el que se define un comando personalizado para el botón (así aprenderás a usar mejor sys:command). Además se amplía la capacidad de la operación saveChanges para detec- tar actualizaciones con éxito, errores y cuando no se han enviado datos al servidor. Todas estas circunstancias se informan a través de un mensaje auto- ocultable en la interfaz de usuario. La página completa es “BindingPorCodigo.aspx”
aDo.NeT Data services. Por ello si quieres crear un servicio WcF o asMX normal que pueda trabajar con contextos de datos normales, estás “solo ante el peligro”.
si analizamos con Fiddler u otra utilidad los datos JsoN que se envían desde el cliente al utilizar el método saveChanges de un dataContext, veremos algo como lo siguiente para la actualización de un producto:
{“changeSet”:[{“action”:1,”item”:{“__type”:”Product:#Datos”,”ProductI D”:3,”ProductName”:”Aniseed Syrup”,”SupplierID”:1,”CategoryID”:2,”Qua ntityPerUnit”:”12 - 550 ml bottles”,”UnitPrice”:”20”,”UnitsInStock”:1 3,”UnitsOnOrder”:70,”ReorderLevel”:25,”Discontinued”:false}}]}
Lo que se envía, por tanto, es una colección de objetos en un parámetro llamado changeSet. estos objetos no tienen un nombre de tipo definido, así que vamos a llamarles nosotros como queramos, por ejemplo Change. Lo que sí tienen son dos propiedades llamadas action e item, que contienen respectivamente el tipo de acción a realizar con el objeto y el objeto en sí que hay que modificar.
Investigando un poco al enviar diferentes tipos de cambios al servidor se llega a la conclusión de que los valores para las acciones pueden ser solamente 3: inserción (0), actualización (1) y borrado (2).
el objeto contenido en la propiedad item es un objeto del tipo que le hayamos pasado al cliente inicialmente, en nuestro caso un objeto Product del contexto de datos de Linq2sQL que hemos utilizado para el acceso a datos.
así que podemos crearnos en el servidor una clase genérica llamada Change, que mapee los miembros de estos objetos JsoN con cambios que nos llegan desde el cliente. en lugar de crear una específica para manejar cambios de productos, la haremos genérica y así nos valdrá para todas las entidades que queramos manejar en el futuro. el código es el siguiente:
Public Class Change(Of T) Public item As T
Public action As TipoOperacion Public Enum TipoOperacion As Integer Insercion = 0
Actualizacion = 1 Borrado = 2 End Enum End Class
es decir, es una simple clase con los mismos campos que nos manda el cliente en formato JsoN. Para la acción, en lugar de tener que acordarnos de qué significa cada número, simplemente lo definimos en una enumeración y así nos resultará más fácil utilizarlos luego en el código.
Bien, sólo nos queda definir entonces el aspecto que debe tener un método de actualización capaz de recibir estos datos, que será el de la figura 7.
Figura 7.- código del método de actualización
como vemos toma como parámetro una lista genérica de objetos Change parti- cularizados para manejar objetos de nuestra entidad Producto. No te dejes confundir por el encadenamiento de “of” para particularizar el código genérico. es una simple lista de objetos que a su vez contienen a otro objeto con los datos a modificar.
en c# la sintaxis sería:
Public int SaveProducts(List<Change<Product>> changeSet)
en realidad esta función podemos definirla para que no devuelva ningún resulta- do (convirtiéndola en un Sub en vB o devolviendo un void en c#) y todo funcionará igual. Lo importante es el parámetro que hay que pasarle para que pueda recibir con éxito los valores modificados.
Una vez definida la función en el servicio ya podremos actualizar desde el cliente conjuntos de datos con el método saveChanges del DataContext.