P. 1
Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)

Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)

4.08

|Views: 101.484|Likes:
Publicado porKrasis Press
Aumenta tu productividad al desarrollar aplicaciones Web 2.0. Este es el tema central del libro y con él conseguirás aprender todo lo necesario para crear aplicaciones de calidad en el menor tiempo posible.

Esta obra está dirigida a programadores de ASP.NET 2.0 que quieren dominar las principales tecnologías que aporta la última edición de la plataforma de desarrollo Web de Microsoft, ASP.NET 4.0. Con él aprenderás a sacarle partido a fondo a todas las nuevas tecnologías aparecidas recientemente, relacionadas con el desarrollo Web.

Cómpralo en papel desde nuestra tienda: www.krasispress.com

(c) Krasis Press 2009
Aumenta tu productividad al desarrollar aplicaciones Web 2.0. Este es el tema central del libro y con él conseguirás aprender todo lo necesario para crear aplicaciones de calidad en el menor tiempo posible.

Esta obra está dirigida a programadores de ASP.NET 2.0 que quieren dominar las principales tecnologías que aporta la última edición de la plataforma de desarrollo Web de Microsoft, ASP.NET 4.0. Con él aprenderás a sacarle partido a fondo a todas las nuevas tecnologías aparecidas recientemente, relacionadas con el desarrollo Web.

Cómpralo en papel desde nuestra tienda: www.krasispress.com

(c) Krasis Press 2009

More info:

Published by: Krasis Press on Sep 24, 2009
Copyright:Traditional Copyright: All rights reserved

Availability:

Read on Scribd mobile: iPhone, iPad and Android.
See more
See less

11/26/2015

Tecnologías ASP .NET 4.

0
(saltando desde la versión 2.0)
José Manuel Alarcón Aguín

TEcNologíAS ASP.NET 4.0 (SAlTANdo dESdE lA vErSióN 2.0)

No está permitida la reproducción total o parcial de este libro, ni su tratamiento informático, ni la transmisión de ninguna forma o por cualquier medio, ya sea electrónico, mecánico, por fotocopia, por registro u otros métodos, sin el permiso previo y por escrito de los titulares del Copyright. Diríjase a CEDRO (Centro Español de Derechos Reprográficos, www.cedro.org) si necesita fotocopiar o escanear algún fragmento de esta obra.

Derechos reservaDos © 2009, respecto a la primera edición en español, por Krasis consulting, s. L. www.krasis.com IsBN: 978-84-936696-1-4 Depósito Legal: VG 961-2009 Impreso en españa-Printed in spain

Agradecimientos
La tarea de escribir un libro nunca es fácil, ni para el autor ni para quienes conviven o trabajan con él. Por eso, entre otras razones, un libro nunca es exclusivamente obra del que lo escribe. así, debo agradecer como siempre a mi familia el que hayan aguantado mis contestaciones secas al teléfono cuando interrumpían la escritura. eso y que no me haya pasado a verlos en unas cuantas semanas, claro. en Krasis, si ya suele ser difícil hablar conmigo, mientras estaba con el libro ha sido poco menos que imposible. Por ello vaya mi agradecimiento también por su paciencia a héctor, María, Pablo, verónica, Fran, Yazmín, Dani y eduardo. a Pablo Iglesias hay que agradecerle especialmente su trabajo con las cubiertas del libro. ¡Preciosas! el bueno de octavio hernández, un sabio de la computación y autor también de Krasis Press, revisó parte del material del libro desde la soleada california. Gracias maestro. La gente de Microsoft Ibérica, y en especial en este caso Beatriz y David, que siempre se acuerdan de mi cuando hay algo de asP.NeT de por medio :-) Finalmente, como siempre, a la principal sufridora de mis delirios frikis, eva. Te ρ = 1 - sin(θ)

vii

Contenido
coNTENido .........................................................................................................................ix PresentACión .....................................................................................................................xiii 1. ASP.NET y SuS vErSioNES .................................................................................1 1.- La historia de AsP.net hasta su versión 2.0 .......................................................1 2.- La versión 3.0 de .net ................................................................................................3 3.- La versión 3.5 de la plataforma .................................................................................4 4.- Un service Pack que es mucho más que un parche ..........................................5 5.- .net 4.0 y Visual studio 2010 ...................................................................................6 6.- De qué trata este libro (y qué deja fuera) ............................................................7 7.- en resumen .......................................................................................................................8 2. FuNdAMENToS dE AJAX......................................................................................9 1.- interfaces de usuario avanzadas ...............................................................................10 2.- Un poco de teoría: el objeto XMLHttprequest ................................................11 3.- Basta de teoría: vamos a la práctica ......................................................................13 4.- Problemas típicos de Ajax y sus soluciones ........................................................16 4.1.- Llamadas fuera de dominio .............................................................................16 4.2.- Gestión de errores y llamadas que no vuelven .......................................17 4.3.- envío de datos al servidor ..............................................................................18 4.4.- Contenidos no actualizados debido a cachés .......................................... 20 5.- Devolución de información: JsOn ..........................................................................21 6.- en resumen.................................................................................................................... 23 3. ASP.NET AJAX EN El SErvidor................................................................... 25 1.- Un primer ejemplo: mejora de una aplicación básica con AJAX ................. 26 2.- Postbacks parciales y repintados parciales de página ...................................... 30 3.- el control scriptManager............................................................................................31 4.- el control UpdatePanel .............................................................................................. 32 5.- Modos de actualización parcial................................................................................ 33 6.- Disparadores ................................................................................................................. 34 7.- indicación de progreso de las llamadas asíncronas ........................................... 37 8.- refrescos parciales periódicos................................................................................. 40 9.- Unicidad del scriptManager ...................................................................................... 42 10.- el control scriptManagerProxy ............................................................................. 43 11.- Gestión de errores AJAX ....................................................................................... 43 12.- incompatibilidades de AJAX................................................................................... 48 13.- AJAX Control toolkit.............................................................................................. 50 4. ASP.NET AJAX EN El NAvEgAdor............................................................ 59 1.- retrollamadas de red a métodos estáticos ......................................................... 60
ix

x

tecnologías AsP.net 4.0 (saltando desde la versión 2.0)

2.-

3.4.5.6.-

1.1.- Análisis de tráfico con un UpdatePanel ......................................................61 1.2.- ejemplo optimizado con retrollamadas de red a métodos estáticos ............................................................................................................... 63 Llamadas a servicios Web .........................................................................................67 2.1.- Definición de un servicio Web AsMX para ser llamado desde Javascript.............................................................................................................. 68 2.2- Creación de la página cliente para llamar al servicio .............................71 servicios de aplicación: Membership y roles desde el navegador............... 75 referencias a scripts en páginas y controles .......................................................81 Optimización de uso de bibliotecas en AsP.net 4.0...................................... 84 5.1.- Combinación de scripts ................................................................................... 87 en resumen.................................................................................................................... 88

5. ENlAZAdo A dAToS EN El NAvEgAdor .......................................... 89 1.- Concepto de plantillas de lado cliente ..................................................................91 2.- Las bases para trabajar .............................................................................................. 93 3.- Definición de la plantilla de productos ................................................................ 94 4.- La clase DataView ....................................................................................................... 95 5.- Pseudo-columnas y atributos especiales .............................................................. 98 6.- Atributos sys condicionales .................................................................................... 100 7.- Atributos code para renderizado condicional ................................................... 101 8.- enlazado de datos en tiempo real ....................................................................... 103 9.- Vistas maestro-detalle: preparar el maestro .................................................... 105 10.- Vistas maestrro-detalle: preparar los detalles ............................................... 106 11.- Devolver los datos modificados al servidor: contextos de datos............ 108 12.- La definición del método de guardado de cambios ......................................110 13.- Historia del navegador ............................................................................................112 14.- en resumen.................................................................................................................115 6. ASP.NET dyNAMic dATA: iNTErFAcES dE dAToS A lA vElocidAd dE lA luZ ......................................................................................117 1.- ¿Qué es Dynamic Data? ...........................................................................................118 2.- nuestro primer proyecto con Dynamic Data ..................................................119 3.- Definir el modelo de datos .....................................................................................121 4.- Añadiendo el modelo a Dynamic Data .............................................................. 122 5.- Plantillas de interfaz de usuario............................................................................ 124 5.1.- Diseccionando una plantilla .......................................................................... 125 6.- Plantillas para entidades .......................................................................................... 129 7.- Plantillas para campos .............................................................................................. 129 8.- Las rutas de las páginas dinámicas....................................................................... 132 8.1.- Parámetros individuales en las rutas ......................................................... 135 9.- Ampliando los metadatos del modelo ................................................................ 136 9.1.- Mejorando la validación de campos........................................................... 138 9.2.- Validaciones personalizadas .......................................................................... 140 10.- Plantillas de campos propias .................................................................................141 11.- Dynamic Data en páginas propias ...................................................................... 143 12.- en resumen ............................................................................................................... 146

Contenido xi

7. FilTrAdo dE dAToS AuToMÁTico coN QuEryEXTENdEr .................................................................................................. 147 1.- el control Queryextender ...................................................................................... 147 2.- tipos de filtros........................................................................................................... 148 3.- Creación de la página base para ejemplo .......................................................... 149 4.- Primer filtro: búsqueda por nombre ................................................................... 152 5.- Filtrado por rangos de valores.............................................................................. 154 6.- Filtrado por valores de propiedades ................................................................... 154 7.- Parámetros de filtrado ............................................................................................. 155 8.- en resumen.................................................................................................................. 157 íNdicE ANAlíTico ..................................................................................159

Presentación
en los últimos años la World Wide Web ha evolucionado mucho. existe un verdadero abismo tecnológico y conceptual entre aquellas primeras páginas estáticas —con cuatro etiquetas para dar formato y unos pocos enlaces— y las actuales aplicaciones Web 2.0 como Google Docs, Facebook o Live Maps. hay tanta diferencia entre ellas como entre los carruajes tirados por caballos y un Fórmula 1. el mundo de mediados de los 90 tampoco era el mismo y, desde los 70 millones de internautas estimados entonces a los casi 1.600 millones de 2009 (InternetWorldstats.com), la cosa ha cambiado mucho. Las diferencias estriban no sólo en lo que salta a la vista, sino también en lo que no se ve. Las expectativas de los usuarios no son los mismas, los lenguajes de programación tampoco. antes era suficiente con mostrar texto plano y unos gráficos, hoy es preciso habilitar una interactividad total entre los elementos de la pantalla y el usuario. cuando todos accedíamos a la WWW usando módems de 28.8 Kbps era aceptable esperar más de un minuto para recibir el contenido estático de una página. Y dábamos gracias a los dioses por ello ;-) hoy en día no sólo debe haber una respuesta inmediata, sino que lo normal es que ni siquiera se evidencie en modo alguno que ha habido un viaje al servidor. Las fronteras entre las aplicaciones de escritorio y las aplicaciones Web son cada vez más difusas. ¡Bienvenidos al mundo de aJaX y las rIa (Rich Internet Applications)! asP.NeT es sin duda (y no es una opinión, sino un hecho) la plataforma de creación de aplicaciones Web más productiva que existe. La base fundamental sobre la que se sustenta esta tecnología y las diferentes características que ofrece, hacen posible esta visión moderna, interactiva y escalable de la red. este libro trata precisamente de esas tecnologías especializadas que marcan la diferencia entre una aplicación Web corriente y otra de la era Web 2.0 y más allá. asP.NeT 4.0 y visual studio 2010 nos traen las últimas mejoras de esta plataforma de desarrollo.

¿A quién va dirigido este libro?
Por lo que acabo de comentar el lector ya se dará cuenta de una cuestión importante: este libro no es para principiantes. el contenido va dirigido a programadores de asP.NeT 2.0 que quieren dominar las principales tecnologías que aporta la última edición, asP.NeT 4.0. se da por hecho que el lector tiene unos conocimientos, cuando menos fundamentales, de esta plataforma. ahora bien, no se da por sentado nada en cuanto a las técnicas que se explican en el interior, de las que se parte de cero para facilitar el aprendizaje.

xiii

xiv

tecnologías AsP.net 4.0 (saltando desde la versión 2.0)

es especialmente interesante que el lector conozca un poco el leguaje Javascript y una pizca de hTML, pues la tecnología aJaX se sustenta sobre ellos y vendrá bien para comprender el código de ejemplo.

¿Qué temas se tratan en el libro?
Una gran parte del contenido se dedica a aJaX y todas las técnicas que hacen que funcione. Pero no te confundas, este no es el típico libro de aJaX que has visto por ahí. va mucho más allá del uso del UpdatePanel que todo el mundo conoce, para adentrarse en la optimización para conseguir aplicaciones escalables. Un primer capítulo se dedica a enseñar los fundamentos de aJaX sin el apoyo de bibliotecas especializadas. Para que nos entendamos mejor: aJaX “a pelo”. esto te ayudará a comprender bien su funcionamiento primordial y podrás responder mejor ante problemas que surjan más adelante en aplicaciones reales apoyadas en código de otros. asP.NeT aJaX es la biblioteca de Microsoft para crear páginas aJaX. el segundo capítulo presenta esta tecnología para sacarle partido sin tener que salirse de las técnicas habituales de todo programador .NeT. aprenderás a utilizar bien sus controles de servidor, que ofrecen una grandísima productividad con un rápido aprendizaje. Te proporcionará grandes ventajas y la usarás mucho, pero no está exenta de problemas. Todo programador preocupado por el rendimiento y la escalabilidad de sus aplicaciones debe ir más allá y no quedarse en este punto. Por eso, el tercer capítulo se centra en las capacidades del lado de cliente de asP.NeT aJaX. se presenta la tendencia actual de las aplicaciones Web a trasladar cada vez más procesamiento al navegador, intercambiado datos directamente con el servidor. aprenderemos lo necesario para poner en práctica esta visión, exponiendo y recibiendo datos a través de servicios que se consumen desde el navegador con Javascript y el apoyo de asP.NeT aJaX. en la cuarta parte del libro llevaremos el concepto de aJaX “puro” al extremo gracias a las nuevas funcionalidades de plantillas para el lado cliente que nos ofrece asP.NeT 4.0. esta nueva tecnología abre las puertas a un desarrollo Web super-eficiente que traslada toda la generación de la interfaz de usuario al navegador, dejando el servidor como un intermediario para mover datos. el quinto capítulo se centra en la nueva tecnología de generación de interfaces de gestión de datos: Dynamic Data. Gracias a ella podemos conseguir en minutos completas interfaces de administración de bases de datos para crear los típicos “Mantenimientos”. Pero como veremos, la tecnología va mucho más allá, proporcionándonos total flexibilidad para hacer lo que queramos en la gestión de datos sin apenas escribir código. Tendrás a tu alcance un nuevo nivel en la escala de la de productividad. La última parte del libro se centra en explicar un conjunto de controles Web destinados a crear interfaces para filtrado de datos. se trata de los QueryExtender

Presentación xv

y las clases relacionadas con éstos. con ellos, nuevos en asP.NeT 4.0, es muy sencillo conseguir avanzados sistemas de filtrado de información sin tener que escribir código. combinándolos con los controles enlazados a datos podemos crear complejas páginas con listados de información en minutos.

las herramientas que necesitas para trabajar
hace poco, después de una charla que impartí, tuve la oportunidad de hablar un buen rato con un par de emprendedores del mundo TIc que se me acercaron. Tras un tiempo de experiencia laboral por cuenta ajena decidieron volar solos, y un par de meses antes habían constituido una empresa para desarrollar aplicaciones Web. Me dijeron que se habían decidido a trabajar con PhP en lugar de con asP.NeT por que “en PhP es todo gratis y para programar con asP.NeT necesitamos pagar licencias a Microsoft.”. ¡Qué confundidos estaban! Y no sólo en esta afirmación, sino con otros muchos mitos y leyendas equivocados que existen sobre asP.NeT y sobre lo que algún día tengo que escribir largo y tendido. Menos mal que dieron conmigo para sacarlos de su error ;-) Para desarrollar con asP.NeT, tanto aplicaciones comerciales como para cualquier otro uso, no es necesario pagar ni un solo euro a Microsoft. visual studio dispone de unas ediciones especiales llamadas Visual Studio Express Edition que son gratuitas y de libre descarga. apenas tienen limitaciones para desarrollar y en concreto la versión especial para desarrollo Web, Visual Web Developer Express, tiene toda la funcionalidad disponible en esta versión gratuita. Para la parte de desarrollo de bases de datos Microsoft ofrece también una versión express de su gestor: SQL Server Express. sus limitaciones son que sólo le está permitido ocupar 1 GB de raM para caché de datos, utilizar un único procesador de la máquina (con los núcleos que tenga éste, da igual) y el tamaño máximo de las bases de datos que puede manejar la licencia es de 4 GB. son unas limitaciones bastante amplias y es difícil llegar a superarlas en aplicaciones comunes en la PYMe. ofrece herramientas adicionales de administración y de reporting entre otras, y es perfecta para cualquier aplicación de gestión o para Internet. Y por supuesto sigue siendo gratis aunque nuestras aplicaciones sean comerciales. Las últimas versiones de Visual Web Developer Express y de SQL Server Express se pueden descargar libremente desde http://www.microsoft.com/express/. Te las recomiendo para practicar las explicaciones del libro. si quieres funcionalidades de trabajo en equipo, desarrollar para office o para móviles o poder depurar aplicaciones en remoto, puedes actualizarte a las ediciones comerciales de visual studio. Toda la información aquí: http://www.microsoft.com/ visualstudio/. Para aplicaciones empresariales de gran tamaño están disponibles las otras ediciones comerciales de sQL server. consulta sus características en http://www. microsoft.com/sqlserver/.

xvi

tecnologías AsP.net 4.0 (saltando desde la versión 2.0)

El código fuente de ejemplo
Todos los ejemplos y demos desarrollados en el libro se pueden descargar desde la web de la editorial. visita www.krasispress.com y busca el libro en el catálogo, bien navegando por las categorías o con la caja de búsqueda. en la ficha del libro existe un enlace para descargar los ejemplos de código. Descárgate el archivo en formato ZIP y descomprímelo en cualquier carpeta de tu ordenador. Para abrir los ejemplos desde visual studio lo mejor es usar la opción Archivo·Abrir·Sitio Web y elegir la carpeta del ejemplo en el que tengas interés. Por el nombre se deduce fácilmente a qué parte del libro corresponden. Para los ejemplos que usan datos he empleado la archiconocida base de datos Northwind. La puedes descargar en diversos formatos desde el sitio de descargas de Microsoft. vete a http://download.microsoft.com y una vez allí introduce la palabra “Northwind” en el cuadro de búsqueda:

Figura 1.- descarga de la base de datos Northwind

se trata de una base de datos muy antigua, por eso pone que es una descarga para sQL server 2000, pero no te preocupes pues te funcionará bien con cualquier versión moderna del gestor de datos. La he usado porque es la más popular entre los programadores de .NeT, y hay una alta probabilidad de que la conozcas ya. existe una versión nueva de esta base de datos, creada por la comunidad, que puedes descargar desde http://www.codeplex.com/NorthwindCommunity/. es un proyecto reciente que trata de actualizar un poco el ejemplo original, pero no te aseguro que los cambios que hayan hecho vayan a funcionar con los ejemplos del libro, así que lo dejo a tu criterio, pero puedes probar.

Presentación xvii

contacto con el autor y la editorial
Puedes encontrarme y contactar conmigo a través de mi blog sobre desarrollo Web en www.jasoft.org. ahí publico constantemente todo tipo de consejos, noticias y vídeos sobre el desarrollo para Internet con asP.NeT. Te recomiendo que lo visites. Me encanta recibir comentarios y críticas constructivas, pero no me gustan los que sólo se acuerdan de mi cuando necesitan algo “¿capisce?” ;-) La web de la editorial es www.krasispress.com. Desde allí puedes ponerte en contacto con el equipo editorial cuando quieras. Dentro de Krasis existe el proyecto campusMvP (www.campusmvp.com) del que probablemente hayas oído hablar. se trata de formación on-line con cursos creados y tutelados por conocidos MvP de Microsoft, para que tú y tu equipo os forméis a vuestro ritmo y desde cualquier lugar preguntándole a los que más saben. en campusMvP tenemos un boletín mensual de noticias, trucos y “frikadas” varias que tiene varios miles de suscriptores encantados de recibirlo. Te recomiendo que te suscribas. También hemos puesto en marcha una página en Facebook (http://go.krasis.com/Facebook) con actualizaciones frecuentes sobre el mundo Microsoft y sus tecnologías, enlaces a artículos interesantes, noticias, vídeos prácticos, ofertas exclusivas para “fans”, etc... si estás en esta red social no olvides hacerte fan de la página.

¡comencemos!
Gracias por tu interés en este libro. espero que el esfuerzo de escribirlo haya valido la pena y que tras haberlo leído estés en condiciones de crear aplicaciones Web de alta calidad, escalables y sacando todo el partido a las últimas tecnologías Microsoft.

cAPíTulo

1

AsP.net y sus versiones

¿Por qué comenzar un libro que no está dirigido a principiantes con una cuestión, en apariencia, tan insignificante como ésta?... Pues porque no es un asunto trivial en absoluto. como veremos, en asP.NeT los saltos en la numeración de las versiones no se corresponden en realidad con los cambios cuantitativos que cabría esperar de éstos, lo que causa gran confusión entre los programadores. Dada la cantidad de características existentes en esta potente plataforma de desarrollo Web, es muy importante saber qué hay disponible en cada versión, e incluso en cada actualización dentro de una versión. Por ello vamos a hacer una composición de lugar antes de ponernos a trabajar.

1.- lA hiSToriA dE ASP.NET hASTA Su vErSióN 2.0
La primera versión de asP.NeT, la 1.0, apareció en el mercado en enero de 2002, junto con la edición inicial de visual studio para la plataforma .NeT. antes de este lanzamiento oficial, hubo diversas Betas en las que aún la tecnología llevaba el nombre de asP+. Éstas estuvieron disponibles para experimentar desde mediados de 2000, por lo que en realidad la plataforma tiene mucho más recorrido del que se desprende de las fechas oficiales. La idea era sustituir al asP 3.0 clásico -por eso le dejaron un nombre parecido, aunque no tengan nada que ver- y ofrecer un entorno de desarrollo para el servidor con grandes ventajas sobre lo existente en el mercado. Un punto importante era tratar de ofrecer los mismos paradigmas de desarrollo que las tradicionales aplicaciones de escritorio, pero aplicados a la Web. esto implicaba sobre todo orientación a eventos y a objetos, uso de lenguajes compilados, independencia automática del navegador y productividad, mucha productividad. con asP.NeT se empezaron a desdibujar los límites entre cliente y servidor en el desarrollo Web.

1

2

tecnologías AsP.net 4.0 (saltando desde la versión 2.0)

Nota:
obviamente sin olvidarnos del inexcusable hecho de que hay una red de comunicaciones de por medio, con lo que ello implica en cuanto a transferencia de datos y velocidad, lo cual sigue siendo muy importante, claro.

el nuevo paradigma de desarrollo traído por los WebForms y la técnica de Postback, realmente innovador y tan característico de asP.NeT, hizo que desarrollar un formulario Web no fuese demasiado diferente de hacerlo para una aplicación de escritorio. Todas las características de productividad de esta primera versión convirtieron rápidamente a asP.NeT en una plataforma muy popular, pues permitía a los desarrolladores conseguir resultados impresionantes con muy poco tiempo y esfuerzo. en abril de 2002, poco después de un año, Microsoft actualizó mínimamente la plataforma sacando asP.NeT 1.1 y visual studio .NeT 2003. en esta ocasión se incorporaron los controles móviles para el desarrollo Web orientado a PDa y teléfonos móviles, así como la validación automática de los elementos de la interfaz de usuario. Una actualización menor. el verdadero punto de inflexión en lo que se refiere al ámbito de desarrollo Web llegó en noviembre de 2005 (febrero de 2006 en españa). en esta fecha se lanza ASP.NET 2.0 junto con visual studio 2005. este sí que constituyó un cambio revolucionario dentro de asP.NeT; uno que influiría definitivamente en todas las versiones posteriores. con todo el feedback recibido por Microsoft durante los cinco años previos, el equipo de desarrollo le dio un vuelco completo a la plataforma. se introdujeron tantos cambios y características que se necesita un libro entero1 para explicarlas. Las más relevantes son las siguientes: • • • • • Nuevo modelo de compilación y separación de código e interfaz, que es un cambio de los propios fundamentos de trabajo de la plataforma. Precompilación y despliegue de sitios Web para obtener un máximo rendimiento desde el primer momento. soporte de sistemas de 64 bits. el modelo de proveedores, con sus implicaciones en cuanto a extensibilidad de la plataforma. acceso a datos, incluso en varias capas soa, sin necesidad de escribir código, gracias a un modelo declarativo. controles enlazados a datos de gran potencia, como las nuevas y mejoradas rejillas GridView, y controles de visualización y edición integrada como FormView o DetailsView.

1

el autor de este libro ha escrito también otra obra dedicada a explicar con detalle todas las tecnologías de asP.NeT 2.0, y que se puede adquirir en www.krasispress.com.

AsP.net y sus versiones 3

Independencia de la interfaz de las páginas de su maquetado (Master Pages) y de su aspecto (Temas y Skins), fomentando la separación más absoluta entre interfaz y funcionalidad. Interfaces de usuario para control de la seguridad mediante controles de arrastrar y soltar. Puedes construir toda la seguridad de tu aplicación sin escribir código alguno. creación de portales personalizables mediante WebParts. Infraestructura de instrumentalización y monitorización de aplicaciones. Nuevas técnicas de localización y globalización de aplicaciones. Páginas asíncronas para sitios altamente escalables. controles de navegación. servicios de personalización de preferencias de usuario.

• • • • • •

este salto a la madurez de la plataforma abrió un nuevo mundo de posibilidades de desarrollo con alta productividad para sistemas Web empresariales, pero hizo que todos los programadores de la versión anterior tuvieran que actualizar sus conocimientos. La cuestión más importante, llegados a este punto, es señalar que el núcleo de ASP.NET es exactamente este mismo, el 2.0, en el resto de versiones que han salido desde entonces. esto tiene una transcendencia fundamental, porque podemos afirmar que si conoces bien ASP.NET 2.0 conoces todo lo necesario para sacarle partido a las siguientes versiones, porque en esencia son la misma, como enseguida veremos. sin embargo, si provienes de la versión 1.x tendrás que aprenderlo todo casi desde cero.

2.- lA vErSióN 3.0 dE .NET
La versión 3.0 de la plataforma .NeT se presentó en sociedad en noviembre de 2006, justo un año después de la anterior. en realidad, esta versión 3.0 no era otra cosa que la plataforma .NeT 2.0 junto con cuatro nuevas aPI especializadas que estaban construidas sobre ésta: • Windows Communication Foundation (WCF): para la creación de sistemas distribuidos basados en conceptos soa (arquitecturas orientadas a servicios). a pesar de lo que pueda parecer por el poco afortunado nombre —que confunde a algunos desarrolladores—, estos servicios no están atados en absoluto a la plataforma Windows, sino que siguen los estándares de la W3c y pueden interoperar con cualquier otro sistema operativo y lenguaje de programación. • Windows Workflow Foundation (WF): que permite el desarrollo de sistemas adaptables basados en flujos de trabajo tanto secuenciales como de estado, persistentes en el tiempo y con otros servicios añadidos.

4

tecnologías AsP.net 4.0 (saltando desde la versión 2.0)

Windows Presentation Foundation (WPF): la nueva aPI para creación de interfaces de usuario vectoriales avanzadas. va mucho más allá de los sistemas tradicionales de ventanas para ofrecer un paradigma mixto entre el escritorio y la Web. Un subconjunto de WPF, multiplataforma y específico para la Web, es lo que se conoce como Silverlight. Windows CardSpace: un sistema cliente para la gestión de identidades en Internet y en la empresa. es, con diferencia, la aPI de la versión 3.0 que menos adopción ha tenido.

si bien todas estas aPI adicionales son muy importantes y abren muchas vías de mejora en la programación de sistemas empresariales, lo cierto es que no dejan de ser añadidos a la plataforma, que dependen de ésta, pero el núcleo sigue siendo la versión 2.0 sin cambios de ningún tipo. en lo que respecta al desarrollo Web y asP.NeT, la versión 3.0 no aporta característica nueva alguna. obviamente, podemos sacarle partido a alguna de estas aPI para añadir características a nuestras aplicaciones Web (por ejemplo, un servicio Web creado con WPF o un flujo de decisión basado en WF), pero en lo que se refiere a la plataforma asP.NeT, en ese momento sigue siendo la misma que había un año atrás.

3.- lA vErSióN 3.5 dE lA PlATAForMA
Mientras tanto, el mundo de las aplicaciones Web empieza a ver la invasión de AJAX. estos desarrollos, a los que dedicamos una gran parte de este libro, se basan en la interacción estrecha con el usuario desde la interfaz del navegador, aparentando en la medida de lo posible que no hay comunicación visible con el servidor. La idea es que las aplicaciones Web se parezcan más aún a las de escritorio, sin recargas de páginas y dando la sensación de que todo ocurre en el equipo del usuario. Microsoft no puede permanecer ajena a esta tendencia que ellos mismos habían creado (como se explica en el primer capítulo de aJaX), así que en enero de 2007 aparece una aPI de manera independiente al desarrollo de la línea principal de .NeT: ASP.NET AJAX, por aquel entonces conocida con el nombre en código de “atlas”. esta aPI para crear interfaces web de alta velocidad de respuesta y sin recarga de páginas, es un éxito instantáneo y se puede instalar paralelamente a asP.NeT trabajando sobre la versión 2.0 existente y con visual studio 2005. Unos meses después, en noviembre de 2007, aparece visual studio 2008, que incluye la versión 3.5 de la plataforma .NeT. esta versión sí que contiene cambios sustanciales en los lenguajes c# y vB y en los correspondientes compiladores. La mayor parte de dichos cambios tienen que ver con añadidos para dar soporte al nuevo lenguaje integrado de consultas LINQ (Language INtegrated Queries). es decir, el núcleo de la plataforma sigue siendo el mismo (versión 2.0), pero con modificaciones en los lenguajes y algunas aPI añadidas, en especial las relacionadas con LINQ.

AsP.net y sus versiones 5

Para acabar de liar las cosas, aunque la versión de la plataforma es la 3.5, la de los lenguajes no coincide. Por ejemplo, la versión de c# aparecida entonces es la 3.0, lo que añade más confusión para los programadores. en este momento se incorpora oficialmente el soporte para aJaX a la parte de desarrollo Web, por lo que a partir de entonces podías contar con que la plataforma tendría incorporado aJaX sin necesidad de instalarlo de manera separada. además, para dar soporte a LINQ desde aplicaciones Web se añadió una biblioteca llamada System.Web.Extensions.dll que incluía tres nuevos controles: LinqDataSource, ListView y DataPager. Por lo tanto, lo que se dio en llamar asP.NeT 3.5 era en realidad lo siguiente: asP.NeT 2.0 + asP.NeT aJaX + 3 controles De nuevo importantes mejoras globales, gracias a LINQ para el acceso a datos, pero el mismo núcleo en lo que al desarrollo Web respecta. La interfaz de trabajo de visual studio 2008 ofrece también algunas características nuevas de productividad para la Web, sobre todo el soporte de Intellisense y depuración para el lenguaje Javascript.

4.- uN Service Pack QuE ES Mucho MÁS QuE uN PArchE
¿Qué tendrá de especial el mes de noviembre para Microsoft? No lo sé, pero si te has fijado, es el mes en el que desde hace muchos años salen todas las grandes versiones de sus plataformas de desarrollo :-). Así, en noviembre de 2008 (como no podía ser de otra manera) aparece el Service Pack 1 para .NET 3.5, es decir, la nueva versión de la plataforma: .NET 3.5 SP1. Tradicionalmente, los Service Pack están pensados para agrupar en una sola descarga una recopilación de ajustes, resoluciones de fallos y parches de seguridad con el objeto de facilitar su aplicación a los usuarios. En realidad no deberían incluir nuevas funcionalidades. Incluso la propia Microsoft los define de esta manera: http:// support.microsoft.com/sp. Pero lo cierto es que en diversas ocasiones el gigante de Redmond los ha utilizado para agregar grandes bloques de funcionalidad a algunos productos. Este fue el caso del SP1 para .NET 3.5. En esta actualización, Microsoft incluyó novedades en todos los ámbitos, siendo la más visible de todas ellas la aparición de Entity Framework, su nuevo ORM para acceso a datos. en el ámbito del desarrollo Web aparecieron también varias características, aunque todas se sustentaban sobre asP.NeT 2.0. entre ellas se encontraban: • • Dynamic Data: un nuevo sistema de creación automática de interfaces de usuario Web a partir de modelos de datos. Control EntityDataSource: para dar soporte a acceso a datos declarativo a través de Entity Framework.

6

tecnologías AsP.net 4.0 (saltando desde la versión 2.0)

ADO.NET Data Services: creación y exposición automática de servicios Web para acceso a datos en modo resT, fundamentalmente basándose en Entity Framework. Enrutamiento: soporte para definición dinámica de rutas virtuales en el servidor y su mapeado a archivos existentes o no. Especialmente pensado para dar soporte a ASP.NET MVC, del que hablaré en breve. Muy sencillo de utilizar. Toda la información aquí: http://msdn.microsoft.com/es-es/library/ cc668201.aspx. Mejoras en AJAX: en concreto, soporte para el historial del navegador, un sistema de combinación de scripts para mejorar los tiempos de descarga de estos archivos, y un control para mostrar vídeos.

No está mal para un simple parche.

5.- .NET 4.0 y viSuAl STudio 2010
Y por fin llegamos al final de la historia (por el momento). a finales de 2009 hacen su aparición visual studio 2010 y la versión 4.0 de la plataforma. en lo que se refiere a visual studio, los cambios son sustanciales. Para empezar, el entorno es completamente nuevo y está basado en WPF, con interesantes mejoras para la escritura de código, la depuración y las superficies de diseño. en lo que se refiere al núcleo de la plataforma y las aPI relacionadas, hay muchos perfeccionamientos y añadidos, si bien los más importantes, en mi opinión, tienen que ver con la introducción de soporte en la plataforma para el desarrollo paralelo. en la actualidad se han popularizado los sistemas con más de un procesador y, sobre todo, los que llevan procesadores de múltiples núcleos (conocidos como manycore), que son hoy el estándar de la industria y que se incluyen hasta en los portátiles de gama más baja. estos sistemas abren grandes posibilidades para el desarrollo de aplicaciones más eficientes y de mayor rendimiento, sacando partido a la capacidad de ejecutar en paralelo múltiples hilos de trabajo. el desarrollo de aplicaciones con múltiples hilos, sin embargo, reviste una gran complejidad para el programador, y con frecuencia se da la paradoja de que aplicaciones con multisubproceso se ejecutan peor en sistemas manycore que en sistemas sencillos más antiguos. Gracias a las extensiones para el desarrollo paralelo incluidas en .NeT 4.0, la creación de este tipo de aplicaciones asíncronas y multi-subproceso se simplifica en gran medida. el nuevo modelo permite a los desarrolladores la escritura de código paralelo que es eficiente, granular y escalable, pero sin tener que lidiar directamente con las dificultades de la creación y sincronización de hilos, y además usando un lenguaje más natural para el programador.

AsP.net y sus versiones 7

otra importante novedad es el soporte para lenguajes dinámicos, del estilo de Lisp, PhP, ruby o incluso Javascript, que ahora son mucho más fáciles de incorporar a la plataforma. en lo que respecta a la parte de desarrollo Web, la principal novedad es, sin duda, el nuevo sistema de plantillas enlazadas a datos en el navegador, parte de la nueva versión de asP.NeT aJaX. esta característica abre la posibilidad de crear aplicaciones aJaX más “puras” con gran trasvase de funciones desde el servidor al cliente. Las plantillas de lado cliente se tratan con profundidad en esta obra. También se ha añadido un nuevo control Chart para generar gráficos, y ciertas mejoras en Dynamic Data. estas últimas también las estudiaremos. adicionalmente, asP.NeT 4.0 incluye una nueva tecnología de desarrollo Web llamada ASP.NET MVC, que se basa en el patrón de diseño Modelo vista-controlador (de ahí el nombre). Éste constituye un sistema de desarrollo Web paralelo al asP.NeT tradicional, usando otro tipo de paradigmas completamente diferentes. esta tecnología ha surgido como un proyecto independiente y ahora se incorpora con pleno soporte a la plataforma en la versión 4.0. hay quien la adora y le ve grandes ventajas, y hay quien opina que es un paso atrás en el modo de desarrollar aplicaciones Web. en cualquier caso, se trata de una tecnología para el desarrollo Web completamente aparte de la tradicional y que en cierta medida “va por su cuenta”. Por este motivo en este libro, centrado en el desarrollo asP.NeT tradicional, no se incluye asP.NeT Mvc. existen en .NeT 4.0 otros muchos otros añadidos y mejoras de diverso calado, muchos de los cuales provienen del sP1 para .NeT 3.5 y que se han incorporado ya como base a la plataforma. en http://tinyurl.com/PDC2008-NETFX4PDF hay para descarga un poster de alta resolución que permite ver todas las novedades de .NeT 4.0 en cada una de sus áreas de una forma resumida y sencilla.

6.- dE Qué TrATA ESTE libro (y Qué dEJA FuErA)
como hemos podido comprobar en esta breve historia de .NeT, existe una cierta complejidad para entender sus versiones y lo que nos proporciona cada una de ellas. Lo que sí podemos afirmar es que, en lo que respecta al desarrollo Web, todas las recientes se sustentan en la funcionalidad proporcionada por la 2.0. Las versiones 3.0, 3.5, 3.5 sP1 y 4.0 incluyen añadidos y mejoras que trabajan con 2.0 por debajo y aportan funcionalidades específicas, como el trabajo asíncrono desde el cliente o la creación de interfaces automatizadas. Por ello, este libro se va a centrar en las principales tecnologías aparecidas desde la versión 2.0 de asP.NeT y hasta la versión actual, que las contiene a todas. así, centraremos gran parte de la obra en el desarrollo de aplicaciones AJAX, empezando por los fundamentos de esta tecnología, la creación de aplicaciones aJaX basadas en el servidor (que es la más común pero la menos óptima) y por fin, el cambio de filosofía que supone transferir gran parte de la lógica desde el servidor al cliente, que es la parte más compleja (y también la más interesante).

8

tecnologías AsP.net 4.0 (saltando desde la versión 2.0)

con ello abarcaremos todo lo que es necesario saber de asP.NeT aJaX, desde la versión descargable independientemente para .NeT 2.0 hasta las novedades aparecidas con .NeT 3.5 sP1 y las últimas técnicas avanzadas de asP.NeT 4.0, por lo que se tendrá una referencia muy completa. otra tecnología que no podíamos dejar fuera es Dynamic Data. aparecida en .NeT 3.5 sP1, es casi una desconocida para la gran mayoría de los programadores Web. esto es una verdadera lástima, pues esta tecnología ofrece grandes posibilidades y mejoras de productividad. con asP.NeT 4.0 se han incluido algunos perfeccionamientos adicionales y se espera que empiece a utilizarse de manera generalizada. en este libro aprenderás lo necesario para sacarle todo el partido. Para terminar he incluido también un completo capítulo sobre los nuevos controles para generación de interfaces de filtrado. existen otras pequeñas mejoras de menor calado que han aparecido con asP.NeT 4.0 para la parte de desarrollo Web. No las he incluido aquí pues son una amalgama de diferentes cosas no relacionadas y hay información exhaustiva en Internet sobre ellas. en mi blog (www.jasoft.org) podrás encontrar información sobre la mayoría en diversas entradas que he ido añadiendo y añadiré en el futuro. hay muchas otras tecnologías que se pueden utilizar en los desarrollos para Internet con .NeT, pero que no son específicas para este propósito: LINQ, Entity Framework, Windows Communication Foundation, Silverlight/WPF, Workflow Foundation... cualquiera de ellas es lo suficientemente extensa y compleja como para merecer un libro dedicado, por lo que no tiene sentido incluirlas en esta obra. si tienes interés en aprender estas tecnologías te recomiendo que visites www.campusmvp.com, en donde encontrarás los mejores libros y cursos del mercado sobre todas ellas.

7.- EN rESuMEN
Una vez que sabemos ubicarnos entre la maraña de versiones y tecnologías para el desarrollo Web, ya estamos en condiciones de comenzar a aprender las principales características de las últimas versiones de asP.NeT. ¡comencemos!

cAPíTulo

2

Fundamentos de AJAX
Desde sus comienzos y hasta hace relativamente poco tiempo las interfaces de usuario de las aplicaciones Web han sido más o menos siempre iguales. Las limitaciones propias del protocolo hTTP (Hyper Text Transfer Protocol) utilizado en las páginas Web han impuesto el tradicional modelo de “petición-respuesta-procesado en el navegador” (a partir de ahora PRP) y vuelta a empezar. Los pasos que sigue una aplicación Web corriente para funcionar suelen ser los siguientes: 1. el usuario solicita una página 2. el servidor devuelve el contenido hTML correspondiente a ésta, normalmente generado a partir de alguna tecnología de servidor (como asP.NeT o PhP) 3. el navegador recibe este hTML, lo procesa y visualiza el contenido resultante. 4. el usuario interactúa con el hTML, envía un formulario al servidor o pulsa un enlace, y se repite el ciclo desde el paso 1: se solicita la página, se devuelve su contenido, se procesa y se visualiza. este proceso es el más natural para hTTP —pensado desde luego para funcionar así— pero tiene el problema de que para obtener una página prácticamente igual a la inicial pero con pequeñas modificaciones es necesario recargar la página completa. esta situación es especialmente común desde que asP.NeT apareció en escena hace ya unos cuantos años, con su novedoso sistema de “postback”. Gracias a este concepto una aplicación Web se programa prácticamente igual que una de escritorio, respondiendo a eventos y accediendo directamente a las propiedades de los objetos. el problema del sistema es que cada uno de los postback al servidor hace que se recargue la página completa, lo cual es percibido de manera evidente por los usuarios (es decir “se nota” el refresco de la página) y crea una sensación poco amigable. además si el retorno de la página tarda más que unos pocos milisegundos se pierde capacidad de respuesta de la página puesto que, durante el proceso de petición-respuesta-procesado, la interfaz del navegador no responde.
9

10

tecnologías AsP.net 4.0 (saltando desde la versión 2.0)

si bien el concepto de “postback” es extremadamente útil, las actuales tendencias en el desarrollo Web hacen que éstas sean cada vez más parecidas a aplicaciones de escritorio. esto implica que los molestos y a la vez inevitables viajes al servidor no deberían ser percibidos por los usuarios. La sensación para éstos debe ser la de que la aplicación está todo el tiempo en su equipo, dejando de lado al servidor, como en una aplicación de escritorio tradicional.

1.- iNTErFAcES dE uSuArio AvANZAdAS
si has usado alguna vez Facebook, hotmail, GMail o alguna aplicación web de última hornada sabes perfectamente de qué estoy hablando. Toma por ejemplo el caso de Facebook. cuando actualizas tu estado o introduces un comentario para un amigo, por detrás se está ejecutando el proceso PrP antes descrito, sólo que tú no lo notas ya que se realiza de manera asíncrona (para no bloquear la interfaz) y actualiza únicamente un trocito de la página, que es el que se ve afectado por tu acción. Imagínate que cada vez que dices “Me gusta” en Facebook se recargara por completo la página entera. se haría insufrible y dudo mucho que tuviera el éxito actual. Lo mismo se aplica a los clientes Web para correo electrónico como los mencionados. Ya nadie concibe pulsar sobre una de tus carpetas de correo (o tags en el caso de GMail) en el lateral, y que de repente se cargue de nuevo la página completa. La experiencia debe ser integrada, como la de cualquier aplicación de escritorio.

Figura 1.- la interfaz de Facebook usa de manera intensiva técnicas AJAX.

Desde que el hTML Dinámico (hTML + Javascript) y las hojas de estilo css hicieron su aparición, cada vez más aplicaciones hacen uso de las posibilidades de modificación “al vuelo” de contenidos que estas tecnologías brindan, consiguiendo los resultados que acabo de describir. en la actualidad todos los navegadores ofrecen soporte para DhTML por lo que si quieres escribir programas complejos de lado

Fundamentos de AJAX 11

de cliente, que interactúen con los contenidos de la página, no implica como antes tener que dejar fuera a parte de tus posibles usuarios. a la combinación de hTML dinámico con tecnologías de servidor se le denomina de manera genérica aJaX. este simpático acrónimo hasta hace poco asociado con el apasionante mundo de la limpieza y con el fútbol, viene del concepto en inglés Asynchronous JavaScript And XML. se basa en el uso de un objeto llamado XMLHttpRequest, presente en todos los navegadores modernos, que como es de imaginar por su nombre sirve para hacer peticiones al servidor de documentos XML a través del protocolo hTTP. Utilizado este objeto se solicitan al servidor datos en formato XML que, una vez recibidos en el navegador, es posible manipular mediante código Javascript y mostrar el resultado dentro de los elementos de la página. esta es la idea original de esta técnica, pero como comprobaremos en breve, de este punto inicial a lo que existe actualmente las cosas han cambiado mucho. aunque casi todo el mundo se piensa que esto de aJaX es un invento de Google y su potente cliente Web de correo electrónico GMail (primera aplicación que en verdad lo popularizó), el objeto XMLHttpRequest apareció originariamente junto a las bibliotecas XML de Microsoft (MsXML) a finales de los años noventa del siglo pasado. el concepto original de esta tecnología fue creado también por Microsoft (se llamaba Remote Scripting), el primer navegador en soportarlo fue Internet explorer y la primera aplicación de este tipo fue outlook Web access (oWa), para acceder a buzones exchange. como veremos enseguida, aparte de que su nombre original haya perdurado por ser simpático, la realidad es que aJaX no siempre es asíncrono ni tampoco siempre usa XML. De hecho lo más habitual es que hoy en día utilice otro formato para transferir los datos: JsoN, que estudiaremos luego. Lo que de verdad constituye el corazón de de esta técnica es el objeto XMLHttpRequest. en este capítulo vamos a estudiar los fundamentos de la tecnología para que no dependas de biblioteca alguna a la hora de implementar estas características, y sobre todo -seamos realistas, casi nunca usarás esto “a pelo”- para que cuando uses una de dichas bibliotecas comprendas lo que hay debajo y puedas determinar posibles problemas. conociendo bien los conceptos subyacentes tendrás muchas más herramientas para sacarle partido a las bibliotecas construidas sobre ellos, como por ejemplo la parte cliente de asP.NeT aJaX u otras muy conocidas como jQuery, YUI, script.aculo.us, MooTools, Dojo, etc...

2.- uN Poco dE TEoríA: El obJETo XMlhTTPrEQuEST
Para sacar partido a aJaX, aparte de tener conocimientos de hTML y Javascript, el primer objeto que debemos conocer a fondo es el mencionado XMLHttpRequest. se trata de una clase disponible en todos los navegadores modernos que permite lanzar desde Javascript peticiones de recursos GeT Y PosT a través de hTTP. Dicho en lenguaje simple, lo que esta clase nos permite es simular mediante código Javascript llamadas al servidor como si éstas hubieran sido hechas por los usuarios.

12

tecnologías AsP.net 4.0 (saltando desde la versión 2.0)

el efecto es el mismo que si el usuario hubiese enviado un formulario o pulsado sobre un enlace, sólo que nuestro código es el que tiene el control sobre ello y lo gestiona de manera independiente al contenido actual de la página (en la jerga se suele decir que lo gestiona “por debajo”). aunque en el caso de Internet explorer se sigue exponiendo su funcionalidad como un objeto ActiveX en el resto de los navegadores (Firefox, safari, chrome, opera...) éste forma parte ya de sus clases nativas. Los métodos y propiedades básicos de esta clase que debemos conocer son los siguientes (los corchetes indican parámetros opcionales): • open(método, UrL, [asincrono], [usuario], [clave]): sirve para abrir una conexión al servidor. No envía ni obtiene información, sólo se conecta. el primer parámetro indica de qué manera queremos enviar la petición al servidor: por GeT o por PosT. el segundo es la dirección Web a la que vamos a llamar. el tercer parámetro es booleano y sirve para indicar si la conexión se realizará de forma asíncrona (por defecto) o no. Los dos últimos parámetros sirven para especificar un nombre de usuario y una contraseña de acceso para recursos protegidos por autenticación básica, si bien esto es bastante absurdo pues estarán escritos en claro en el Javascript, por lo que raramente se utilizan. send(contenido): envía una petición. si es un envío por PosT se pueden incluir los datos a enviar en su único parámetro, si no se usa un nulo. abort(): cancela un envío/petición abierto previamente. onreadystatechange: a esta propiedad se le asigna una referencia a un método Javascript que será llamado automáticamente cuando se descargue del todo la UrL remota (cuando se llama a ésta asíncronamente). readyState: informa del estado de la petición: o o o o • 0=no iniciada 1=cargando 2=terminada pero sin procesar 4=completada.

• • •

status: código de estado hTTP resultado de la petición: por ejemplo 200 (éxito), 404 (no encontrado), etc... se corresponden con los códigos de estado de hTTP (los puedes consultar aquí: http://www.w3.org/Protocols/HTTP/ HTRESP.html). statusText: mensaje de información correspondiente al estado anterior. responseXML: documento DoM que representa el XML devuelto por la petición. sólo se utiliza si lo que esperamos obtener es un documento XML.

• •

Fundamentos de AJAX 13

en la actualidad no se utiliza casi nunca pues el XML se ha abandonado en aJaX a favor de JsoN, que luego veremos. • responseText: el contenido puramente textual del recurso remoto. es la que usaremos habitualmente para obtener el contenido del recurso solicitado.

aunque dispone de algunos métodos y propiedades más, con estas tendremos suficiente para el 99% de los casos que nos vamos a encontrar.

3.- bASTA dE TEoríA: vAMoS A lA PrÁcTicA
veamos cómo se usa la clase XMLHttpRequest en la práctica con un ejemplo sencillo. consideremos algo simple como el ejemplo de la figura:

Figura 2.- Ejemplo de lista desplegable auto-completada con AJAX

hay una lista desplegable que contiene una serie de categorías. al elegir una de éstas, en la lista de al lado deberán aparecer los elementos que contiene rellanándolos dinámicamente con lo que nos indique una página en el servidor, pero sin recargar la página. este ejemplo es muy sencillo pero nos ayuda a centrarnos sólo en la parte que nos interesa, que es la de cliente y en cómo realizar las llamadas aJaX. Lo que debemos hacer para que esto funcione es responder a los eventos de cambio de selección de la primera lista y lanzar “por debajo” una petición a una página del servidor que nos devolverá los elementos de la lista secundaria. en un ejemplo real los elementos se obtendrían, en la página del servidor, seguramente desde una base de datos.

14

tecnologías AsP.net 4.0 (saltando desde la versión 2.0)

Lo primero que debemos hacer para lanzar una llamada al servidor desde nuestro código Javascript es obtener una referencia a un objeto de la clase XMLHttpRequest que será el que utilizaremos. Dado que Internet explorer es diferente a los demás navegadores en este aspecto, debemos distinguir con quién estamos trabajando para poder instanciarlo. Podemos encapsular esta funcionalidad con el código del listado 1:

listado 1
function getHttpRequest() { var httpReq; //Si es Mozilla, Opera, etc... if (window.XMLHttpRequest) { httpReq = new XMLHttpRequest(); } else //Internet Explorer lo expone como control Active X { httpReq = new ActiveXObject(“Microsoft.XMLHTTP”); } }

así dispondremos de una función getHttpRequest que nos devolverá una instancia del objeto XMLHttpRequest independientemente del navegador. Una vez que tenemos una referencia al objeto usaremos sus métodos y propiedades para lanzar una petición a la página de servidor que nos interese, por ejemplo así:
http = getHttpRequest() http.onreadystatechange = finCarga; http.open(“GET”, “http://www.miserv.com/misdatos.aspx”, true) http.send(null);

en la segunda línea, y antes de llamar a la página del servidor, establecemos una referencia a la función que se llamará automáticamente cuando cambie el estado de la petición que vamos a lanzar. ello no implica que ésta tenga éxito o que sea llamada sólo cuando termine, como veremos enseguida. La tercera línea abre el conducto de petición al servidor para cargar una determinada UrL de modo asíncrono (true en el tercer parámetro). en este caso usará el método GeT pues no enviamos datos al servidor. Por fin debemos hacer la llamada (el open de la línea anterior no la hace sólo la prepara), usando para ello el método send. se le pasa un nulo porque no enviamos ninguna información extra (es una petición GeT). el hecho de que sea una llamada asíncrona hará que se devuelva el control inmediatamente a Javascript al pasar por esta línea, por lo que podríamos tener más código a continuación para llevar a cabo más tareas sin que se viera interrumpida la ejecución ni la interacción con el usuario. Podríamos usar llamadas síncronas ( false en el tercer parámetro) para lanzar varias

Fundamentos de AJAX 15

llamadas seguidas con la certeza de que se ejecutarán en un determinado orden. es decir, como vemos, aJaX en realidad no siempre debe ser asíncrono. obviaremos de momento el código de la página del servidor que podría ser cualquiera (acceder a una base de datos para obtener los elementos, leer un archivo o la memoria, etc...). Lo único verdaderamente importante es ponernos de acuerdo en cómo la página del servidor va a devolver los resultados de la petición. Podemos complicarlo todo lo que queramos usando XML o cualquier otra notación que consideremos oportuna. Más tarde retomaremos este tema. De momento para acabar con el ejemplo vamos a suponer simplemente que el servidor nos devuelve una lista, separando con comas los elementos que se desean mostrar en el control secundario. como hemos visto se define una función llamada ‘fincarga’ que es llamada de manera automática al ir cambiando el estado de la petición. veamos cómo es su aspecto en el listado 2:

listado 2
function finCarga() { if (http.readyState == 4) //4: completado { if (http.status == 200) //200: OK { res = http.responseXML; Procesarespuesta(); } else //Se produjo un error { alert(“No se pudo recuperar la información: “ + http.statusText); } } }

Lo único que hacemos aquí es detectar cuándo se ha terminado la petición (readyState será igual a 4 como hemos visto antes) y que ésta haya sido una petición exitosa (el código de estado hTTP debe ser 200). si el código hTTP es 404 (no encontrado), 500 (error en el servidor) u otro cualquiera se advierte con un mensaje al usuario. en caso positivo lo único que hacemos es anotar la respuesta del servidor en una variable global de la página (res en el ejemplo) y procesar el resultado adecuadamente. en este caso se separan los elementos con las comas y se carga la lista secundaria. Dado que es un código Javascript trivial y no aporta nada al tema que nos ocupa no lo he incluido aquí, pero lo puedes ver en el archivo con los ejemplos del libro. Lo único que se usa normalmente al mostrar los resultados son los conocidos métodos getElementsByTagName y getElementByID de hTML dinámico y del DoM. estudia el ejemplo incluido en el ZIP para ver exactamente como se ha hecho.

16

tecnologías AsP.net 4.0 (saltando desde la versión 2.0)

este ejemplo tan sencillo constituye en realidad todo lo que es necesario saber sobre los fundamentos de funcionamiento de aJaX.

4.- ProblEMAS TíPicoS dE AJAX y SuS SolucioNES
ahora que ya conocemos los rudimentos de aJaX vamos a ver cuáles son los principales problemas que nos podemos encontrar al usar estas técnicas, y que en ocasiones pueden ser complicados de detectar. Probablemente, serán los mismos que nos encontraremos si utilizamos alguno de los paquetes específicos para aJaX como asP.NeT aJaX y las otras mencionadas, por lo que debemos ser conscientes de ellos. Los más importantes son los siguientes: 1. Llamadas fuera del dominio. 2. Llamadas que producen errores o que no vuelven jamás. 3. envío de datos al servidor. 4. contenidos no actualizados debido a cachés.

4.1.- llamadas fuera de dominio
Una vez que uno empieza a juguetear con las posibilidades de aJaX enseguida se nos ocurren ideas geniales para sacarle partido. La más obvia, claro está, es la de utilizar las técnicas para acceder desde el cliente a ciertos servicios Web ajenos de utilidad ubicados en Internet. así, dado que los servicios Web están basados en XML, es muy fácil procesar lo que devuelven con las técnicas descritas para, por ejemplo, realizar búsquedas en amazon con su aPI, seguir una subasta en eBay, enviar “posts” a nuestro blog, consumir fuentes rss, etc... Todo esto es estupendo pero tiene un gravísimo problema: los navegadores, por cuestiones de seguridad, bloquean todas las peticiones realizadas mediante XmlHttpRequest a dominios que no sean el que aloja la página desde la que se está usando. en realidad se trata de una restricción bastante lógica y que aparece en otras partes del navegador, como las cookies, el acceso a variables de Javascript entre marcos, los objetos Flash o los applets de Java. Pero esto, claro está, supone una limitación importante para ciertos tipos de aplicaciones aJaX que podríamos desarrollar, como las de los ejemplos comentados. La pregunta ahora es entonces: ¿cómo solventamos esta situación? en Internet explorer basta con bajar el nivel de seguridad para que ya funcione correctamente, pero no es una buena solución (no le puedes pedir esto a tus usuarios). en otros navegadores (Firefox, opera, chrome y safari) no hay forma de saltarse esta restricción. existe una salvedad en Firefox que consiste en firmar digitalmente el Javascript que usas, pero tampoco vale de mucho pues sólo funcionaría en este navegador. La única forma de solucionarlo de manera independiente al navegador es, aunque sea de Perogrullo, hacer que no dependa de éste, es decir, llevarnos el problema al

Fundamentos de AJAX 17

servidor. Para ello lo que debemos hacer es construir un servicio proxy que esté en nuestro servidor (al que sí podremos llamar con aJaX) y que sea éste el que se encargue de realizar la llamada a otros dominios devolviendo el resultado a nuestro Javascript (directamente o pre-procesándolo de algún modo). en .NeT esto implica generalmente crear un manejador de peticiones con extensión .ashx o un servicio Web propio que se encargue de realizar por nosotros las peticiones que nos interesen. ¡Mucho ojo con esto!. Normalmente este tipo de servicios -al igual que los que se encargan de leer archivos de disco de manera genérica y otros similares- son un verdadero peligro de seguridad si no los programamos bien. si optas por esta solución lo mejor es que tomes varias precauciones de cara a la seguridad: tener muy acotados los servicios o UrLs a los que se puede llamar desde el proxy. Lo mejor es identificarlos a cada uno con un número o código decidiendo a cuál se llama (con una cláusula switch en c# o Select Case en vB.NeT), nunca poniendo la URL directamente en la llamada desde JavaScript. otra medida adicional es tratar de identificar al script llamante de alguna manera: mediante una cabecera que te debe enviar, comprobando el dominio del “referer” y cosas similares. está claro que un cracker experimentado se puede saltar esto pero le dará bastante trabajo y elimina de un plumazo a los aficionados que quieran hacer uso ilícito de tu servicio. si puedes limita el número máximo de llamadas seguidas que se puede hacer desde una determinada IP o, mejor, en una determinada sesión de servidor. Toda precaución es poca.

4.2.- gestión de errores y llamadas que no vuelven
No podemos asumir que las llamadas que hagamos al servidor van a funcionar siempre. Puede haber un error en el código del servidor, es posible que haya cambiado la UrL y que no aparezca la página que llamamos, o que haya errores de permisos, etc... Lo que pase en el servidor está fuera de nuestro control. ante eso hay que estar preparado. La forma de controlar estas situaciones es, como en cualquier componente de comunicaciones por hTTP, a través del código de estado que devuelva el servidor. Todo esto ya se había apuntado antes y se había tenido en cuenta en el código del listado 2. Podríamos afinar más en el mensaje de error y devolver uno diferente según el código de estado. hay, sin embargo, una situación menos frecuente pero más peligrosa que se puede producir: que la llamada asíncrona al servidor no vuelva o no lo haga en un tiempo razonable, es decir que se produzca lo que se denomina un timeout. ¿Qué hacemos en ese caso? No podemos contar con la notificación de final de carga puesto que, al no regresar la llamada no saltará, así que el listado 2 no nos sirve. Lo que se hace en estos casos es establecer un temporizador con el tiempo máximo que deseemos esperar, para que al cabo de ese intervalo la petición sea anulada directamente, sin esperar más que llegue la respuesta. Podemos verlo en el listado 3:

18

tecnologías AsP.net 4.0 (saltando desde la versión 2.0)

listado 3
http = getHttpRequest() http.onreadystatechange = finCarga; http.open(“GET”, “http://www.miserv.com/misdatos.aspx”, true) var tmrAnular = setTimeout(“AnularPeticion()”, 20000); //20 segundos http.send(null); function AnularPeticion() { http.abort(); } function finCarga() { if (http.readyState == 4) //4: completado { clearTimeOut(tmrAnular); if (http.status == 200) //200: OK { res = http.responseXML; Procesarespuesta(); } else //Se produjo un error { alert(“No se pudo recuperar la información: “ + http. statusText); } }

}

se ha modificado el código de llamada anterior para añadir la creación de un temporizador que se encarga de anular la petición al pasar un tiempo determinado (en este caso de 20 segundos pero puede ajustarse a cualquier otro valor). Nótese también como en el evento de fin de carga eliminamos el temporizador (que ya no nos hace falta) cuando la petición termina de procesarse, en caso de que regrese.

4.3.- Envío de datos al servidor
Normalmente cuando pensamos en aJaX, es decir, en llamadas asíncronas a servicios, lo hacemos desde el punto de vista de obtener información: llamo a una página que me devuelve unos valores y los muestro en la interfaz de usuario. aunque este

Fundamentos de AJAX 19

es el uso más común de aJaX lo cierto es que también es muy útil usarlo en el sentido inverso, para enviar datos al servidor. Las utilidades y necesidades que cubre son múltiples y de hecho hay muchos sistemas que le sacan partido. La forma más sencilla y directa de enviar datos simples al servidor es incluirlos en la UrL a la que llamamos como parámetros GeT: urldestino.aspx?Parametro1=1234&Parametro2=5 aunque esto puede servirnos para cosas muy sencillas no es lo que necesitaremos en la mayor parte de los casos. Lo habitual es que la información haya que enviarla con el método PosT. La principal diferencia entre GeT y PosT estriba en que el método GeT hace una sola llamada al servidor, solicitando una página y enviando algunos parámetros de datos en la propia petición. PosT por el contrario realiza dos conexiones al servidor. en la primera solicita una UrL y en la segunda envía los datos. Por GeT lo máximo que se puede enviar son 2 kB de información, mientras que por PosT no existe esta limitación. Para enviar datos al servidor mediante PosT nuestro código aJaX sería similar al siguiente:
http = getHttpRequest() http.onreadystatechange = finCarga; http.open(“POST”, “http://www.miserv.com/misdatos.aspx”, true) http.send(‘Parametro1=1234&Parametro2=5’);

con esto no hemos ganado demasiado. ahora se envían los datos por PosT (sólo cambia el primer parámetro de open) pero los hemos tenido que introducir en el método send en lugar de en la propia UrL. esto sólo simularía el envío de parámetros mediante PosT desde un formulario hTML, aunque por otro lado en ocasiones puede ser lo que queramos. Lo habitual sin embargo es que, en lugar de enviar parámetros, queramos enviar información pura y dura del tamaño que sea preciso, que es para lo que suele usarse PosT. esto se puede conseguir modificando ligeramente el código anterior para incluir una cabecera que indique al servidor que lo que le llega son, precisamente, datos (línea 3 del siguiente fragmento):
http = getHttpRequest() http.onreadystatechange = finCarga; http.setRequestHeader(‘content-type’, ‘application/x-www-formurlencoded’); http.open(“POST”, “http://www.miserv.com/misdatos.aspx”, true) http.send(‘Aquí ahora mando la información que quiera al servidor’);

con esto nuestro problema queda resuelto.

20

tecnologías AsP.net 4.0 (saltando desde la versión 2.0)

4.4.- contenidos no actualizados debido a cachés
cuando se envía una petición hTTP es posible que, si la caché del lado servidor no está correctamente configurada, el navegador realice su propia caché local. Por lo tanto la próxima vez que realicemos una llamada a la misma UrL, el navegador en lugar de hacerla sacará el mismo resultado anterior de esa caché local, y por lo tanto la llamada no llega al servidor jamás. o puede que exista un proxy-caché por el medio (Teléfonica por ejemplo las ha utilizado tradicionalmente en sus servicios de acceso a Internet) que almacena peticiones anteriores y por lo tanto obtenemos únicamente una copia, sin realizar la llamada al servidor real. eso muchas veces es lo que querremos para ahorrar procesamiento y será maravilloso, pero lo habitual es que sea una maldición ya que evitará que obtengamos datos actualizados. a la hora de enviar datos por PosT no hay problema porque no actúa nunca la caché. el problema, si se da, está en las peticiones GeT, por otro lado las más habituales. si el servidor tiene bien configurada la caché (es decir, indica cuándo caducan los contenidos o marcamos en IIs que éstos caduquen inmediatamente) no deberíamos experimentar fallos, salvando lo comentado respecto a los proxy-caché de algunos proveedores. si queremos asegurarnos de que la petición va a llegar a su destino podemos hacer fundamentalmente dos cosas: 1. agregar una cabecera que indique que se debe obtener el contenido siempre que éste sea posterior a una fecha, por ejemplo así:
http.setRequestHeader(‘If-Modified-Since’, ‘Wed, 1 Jan 1972 00:00:00 GMT’);

Indicaremos siempre una fecha anterior a la actual como la del ejemplo y así siempre se pedirá la última versión al servidor. 2. añadir un número aleatorio (o cadena) a la UrL de cada petición. en este caso suele funcionar muy bien el agregarle una marca temporal, es decir, añadir a continuación la fecha y hora actuales, de modo que cada una de las peticiones que se hagan va a ser diferente y por lo tanto los caché que existan por el medio tienen que repetir siempre la petición. Por ejemplo:
http.open(“POST”, “http://www.miserv.com/misdatos.aspx?pasacache=” + new Date().getTime(), true);

se le añade un parámetro que lleva como valor la fecha y hora en formato numérico (es decir, un número muy largo y que varía varias veces cada milisegundo), por lo que es muy difícil que se den dos peticiones idénticas incluso a través de un proxy-caché. además ese parámetro extra de nombre inventado que nosotros le añadimos no debería afectar en absoluto a la llamada puesto que no está siendo

Fundamentos de AJAX 21

tenido en cuenta por la aplicación. esta segunda técnica es la más fiable, aunque un poco más tediosa de implementar.

5.- dEvolucióN dE iNForMAcióN: JSoN
en el ejemplo anterior hemos hecho que la página del servidor devuelva ciertos valores que en este caso tenían formato XML, pero que podrían tener perfectamente otra configuración distinta, como por ejemplo simples valores separados por comas. si bien esto puede ser suficiente en los casos más sencillos, en otras ocasiones necesitaremos manejar estructuras de datos más complejas. Dado que hTTP es un protocolo basado en texto, el recurso al que llamemos en el servidor debe devolver siempre texto (o sea, no puede ser una imagen o un archivo binario, que para transferirse se codifican de una forma especial -Base64- para convertirlos en texto). este texto devuelto puede tener cualquier formato: texto plano, XML, código Javascript o incluso hTML. en este último caso podemos obtener desde el servidor un contenido hTML completo que se debe escribir en una zona de la página (por ejemplo dentro de un <DIv> o un <sPaN>). sin embargo la mayor parte de las veces lo que tendremos que procesar es alguna estructura de datos. Ya hemos mencionado que la ‘X’ de aJaX significa XML, pues era el formato de moda a finales de los 90 y principios de los 2000 y se usaba para todo. como se demostró en nuestro sencillo ejemplo este formato no es necesariamente el que se va a devolver. De hecho hoy en día el XML se usa muy poco a la hora de representar los datos textuales devueltos desde el servidor en páginas aJaX. el motivo de esto es principalmente que los datos representados con XML, si bien son ricos en estructura, hacen que el resultado devuelto ocupe mucho debido a las etiquetas de apertura y cierre de los diferentes nodos. Gracias al DoM es fácil procesar la información jerárquica que se representa mediante XML, aún así debería existir algún método más directo, más rápido y que ocupe menos ancho de banda. como alternativa a XML surgió un nuevo formato programable llamado JsoN (pronunciado “yeison”) que lo reemplaza con mucha ventaja en la mayor parte de los casos. JsoN es el acrónimo de JavaScript Object Notation, y como su propio nombre indica permite representar objetos (en realidad estructuras complejas) en forma de código Javascript que luego podemos evaluar. JsoN tiene varias ventajas sobre XML, a saber: 1. ocupa mucho menos al transmitirlo por la red. 2. el acceso en el navegador a los elementos de datos representados es directo y sin necesidad de procesamiento farragoso usando el DoM o expresiones regulares, ya que se trata directamente de Javascript que se puede interpretar. 3. Los datos pueden ir colocados en cualquier posición. consideremos el siguiente código XML que representa los datos de un cliente:

22

tecnologías AsP.net 4.0 (saltando desde la versión 2.0)

<cliente> <nombre>José Manuel</nombre> <apellidos>Alarcón Aguín</apellidos> <empresa>Krasis</empresa> <telefono>902 876 475</telefono> <edad>37</edad> </persona>

ahora consideremos la misma representación en JsoN:
{ “nombre” : “José Manuel”, “apellidos” : “Alarcón Aguín”, “empresa” : “Krasis”, “telefono” : “902 876 475”, “edad” : 37

}

crearlo es muy fácil pues es sintaxis Javascript normal. en www.json.org es posible encontrar una explicación completa de esta notación. La Wikipedia también tiene una información muy completa sobre ello. como se puede comprobar ocupa menos que el XML equivalente, es igual o incluso más fácil de leer que éste, y permite usar datos nativos y no sólo cadenas para representar los valores. en estructuras de datos más complejas se puede apreciar más todavía el ahorro de datos que implica. De todos modos lo más espectacular de JsoN es lo fácil que resulta usarlo. Basta con escribir lo siguiente:
var cliente = eval(res);

siendo ‘res’ el nombre de la variable que contiene el JsoN obtenido del servidor. es decir, lo único que hacemos es procesar la expresión JsoN. al hacerlo obtenemos en la variable ‘cliente’ un objeto cuyas propiedades son los datos que queremos manejar. De este modo lo único que tenemos que hacer para leerlos es escribir directamente expresiones como esta:
alert(“El nombre de la empresa es “ + cliente.empresa);

Más fácil imposible. Nada de recorrer una jerarquía XML con el DoM o ir buscando nodo a nodo en el contenido. se convierte en Javascript puro y utilizable nada más llegar desde el servidor. el uso de JsoN como formato de intercambio sólo tiene dos problemas aparentes. el primero de ellos es el más obvio: generar XML con c# o vB.NeT es muy fácil pero generar JsoN ha requerido tradicionalmente un cierto trabajo por nuestra parte. Para evitárnoslo las últimas versiones de .NeT (3.5 o superior) incluyen la posibilidad

Fundamentos de AJAX 23

de que los servicios Web y WcF (Windows Communication Foundation) generen sus resultados directamente en JsoN. existen además bibliotecas especializadas en generar JsoN si estamos interesados en hacerlo de manera manual. Por ejemplo, una muy famosa es Jayrock (http://jayrock.berlios.de) que permite convertir objetos .NeT directamente a su representación JsoN. el otro problema es tal vez menos evidente pero más importante: la seguridad. Dado que se usa una expresión ‘eval’ para convertir el texto en objetos Javascript podría utilizarse de manera malintencionada para inyectar código peligroso en la página al efectuar la evaluación. Para evitarnos este peligro con código en el que no confiamos, en JsoN.org tenemos un script (http://www.json.org/json.js) que extiende las cadenas de Javascript para convertirlas a JsoN verificando la sintaxis y evitando todo aquello que no sean datos. si incluimos este script en nuestra página en lugar de utilizar ‘eval’ podemos escribir:
var cliente = res.parseJSON();

para obtener el mismo resultado. además nos ofrece la funcionalidad inversa de la siguiente manera:
var miCadena = cliente.toJSONString();

esto nos puede servir para enviar datos JsoN al servidor, para almacenar un objeto en una cookie, etc...

6.- EN rESuMEN
en este capítulo hemos aprendido los fundamentos de aJaX así como los principales problemas que nos podemos encontrar al utilizarlo. La comprensión de todo ello nos va a resultar útil aunque no usemos estas técnicas de “bajo nivel” sino que recurramos a un kit especializado como asP.NeT aJaX, o cualquiera de las múltiples bibliotecas de Javascript con soporte aJaX existentes en el mercado. Lo habitual será que siempre implementemos características aJaX en nuestras páginas usando alguna biblioteca especializada. en el mundo Microsoft emplearás con toda seguridad la parte cliente de asP.NeT aJaX, así como la biblioteca de código abierto JQuery, que se soporta oficialmente a partir de visual studio 2010. en el próximo capítulo vamos a estudiar las principales características de asP.NeT aJaX en el lado de servidor, que se traducen en mejoras en el lado del cliente.

cAPíTulo

3

AsP.net AJAX en el servidor
como hemos visto en el capítulo anterior, aJaX es un gran aliado a la hora de mejorar la usabilidad y la capacidad de respuesta de la interfaz de nuestra aplicación Web. Las técnicas aJaX nos evitan tener que refrescar la página completa cada vez que se genere un evento de servidor, lanzando las peticiones en segundo plano y refrescando sólo las partes de la interfaz que sean apropiadas en cada caso. conseguir todo esto directamente con nuestro propio código Javascript en el navegador puede convertirse en una tarea tediosa, que necesita bastante trabajo y es propensa a errores. Para evitarnos estos problemas y facilitarnos la tarea asP.NeT incluye una biblioteca especializada en creación de aplicaciones aJaX. Microsoft, en un alarde originalidad, llamó a esta biblioteca asP.NeT aJaX. su nombre anterior, cuando estaba todavía en versión Beta, era “atlas” y la verdad es que a casi todo el mundo le gustaba más :-)
Nota:
como ya comenté en el capítulo de introducción, asP.NeT aJaX está incluido en.NeT de manera nativa a partir de su versión 3.5 (y versión 2008 de visual studio) y en todas las versiones posteriores. en asP.NeT 2.0 la biblioteca se debe descargar e instalar por separado para poder utilizarla. Puedes encontrarla en www.asp.net/ajax. No hay soporte para aJaX en las versiones 1.0 y 1.1 de asP.NeT, ya completamente desfasadas.

La biblioteca de aJaX se divide en dos partes claramente diferenciadas pero que trabajan de manera conjunta para dotarnos de funcionalidad: · Biblioteca de servidor: contiene un conjunto de controles Web que abstraen de manera muy efectiva las tareas que deberíamos hacer normalmente en el cliente. Generan todo el código necesario para gestionar de manera asíncrona las llamadas al servidor y las actualizaciones. Los dos más importantes son el control ScriptManager y el UpdatePanel. en la figura se pueden observar los controles dentro del cuadro de herramientas de visual studio.

25

26

tecnologías AsP.net 4.0 (saltando desde la versión 2.0)

Figura 1.- controles de servidor para AJAX

· Biblioteca de lado cliente: se trata de una serie de scripts en Javascript que extienden las capacidades de este lenguaje para facilitar la creación de código en el navegador (código de lado cliente). La parte servidora se basa en estas funciones para generar su funcionalidad. ofrece, por ejemplo, capacidades avanzadas de orientación a objetos para Javascript, creación de controles reutilizables, capacidad para crear scripts localizados, llamadas a servicios Web desde el cliente y, por supuesto, características para el control de las comunicaciones con el servidor. La funcionalidad de lado cliente es, en verdad, la parte más compleja de asP. NeT aJaX y le dedicaremos dos capítulos del libro. en este nos vamos a centrar en la funcionalidad generada desde el servidor.

1.- uN PriMEr EJEMPlo: MEJorA dE uNA APlicAcióN bÁSicA coN AJAX
Para empezar a abrir boca con asP.NeT aJaX vamos a reproducir el ejemplo sencillo del capítulo anterior de fundamentos usando asP.NeT. veremos cómo se comporta al tratarse de una aplicación asP.NeT normal y luego lo fácil que es convertirlo en una aplicación aJaX. Nos servirá para conocer los fundamentos de la parte servidora de la aPI de aJaX. crea un nuevo sitio Web con visual studio y en la página por defecto añade dos controles DropDownList, uno a continuación del otro separados con un par de espacios en blanco. en el primero de ellos habilita el “auto postback” de modo que se genere automáticamente un evento SelectedIndexChanged en el servidor cada vez que se seleccione un elemento diferente de la lista. Usa la opción de editar elementos en su menú de tareas para añadir cuatro categorías: revistas, Blogs, empresas y Libros.

Figura 2.- El menú de tareas del control dropdownlist

AsP.net AJAX en el servidor 27

ahora vamos a responder a su evento de selección para introducir en la segunda lista los elementos correspondientes a la categoría seleccionada. haz doble clic en el control DropDownList1 para obtener la definición del manejador de eventos para su evento SelectionIndexChanged y escribe el siguiente código:
Protected Sub DropDownList1_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles DropDownList1. SelectedIndexChanged Select Case DropDownList1.SelectedValue.ToLower() Case “revistas” DropDownList2.Items.Clear() DropDownList2.Items.Add(New ListItem(“dotNetMania”)) DropDownList2.Items.Add(New ListItem(“MSDN Magazine”)) DropDownList2.Items.Add(New ListItem(“CodeProject”)) Case “blogs” DropDownList2.Items.Clear() DropDownList2.Items.Add(New ListItem(“www.jasoft. org”)) DropDownList2.Items.Add(New ListItem(“www.geeks.ms”)) DropDownList2.Items.Add(New ListItem(“weblogs.asp. net”)) Case “empresas” DropDownList2.Items.Clear() DropDownList2.Items.Add(New krasis.com]”)) DropDownList2.Items.Add(New microsoft.com]”)) DropDownList2.Items.Add(New [www.plainconcepts.com]”)) Case “libros” DropDownList2.Items.Clear() DropDownList2.Items.Add(New castigo”)) DropDownList2.Items.Add(New soledad”)) DropDownList2.Items.Add(New End Select End Sub

ListItem(“Krasis [www. ListItem(“Microsoft [www. ListItem(“Plain Concepts

ListItem(“Crimen y ListItem(“Cien años de ListItem(“El Quijote”))

con esto tenemos suficiente para demostrar la diferencia entre una aplicación Web corriente y una con soporte para aJaX. ejecuta la aplicación pulsando F5 (acepta el diálogo que te avisa que se va a modificar web.config para dar soporte a la depuración) y cuando se abra el navegador juega un poco con la interfaz cambiando la selección en la primera lista.

28

tecnologías AsP.net 4.0 (saltando desde la versión 2.0)

como puedes comprobar, cada vez que eliges un elemento nuevo en la lista 1, se produce un postback al servidor y al regresar la página, ésta se recarga y muestra los elementos apropiados en la segunda lista. Los efectos molestos de esta aplicación tan sencilla son fáciles de ver: 1. Durante la recarga de la página se produce un claro parpadeo, correspondiente al borrado de la página original y la subsiguiente recarga de ésta con los nuevos valores. si usas Internet explorer además oirás un sonidito (como un “clac”) en cada recarga. 2. La primera vez que haces una selección normalmente la página tarda un poco más de lo habitual en ejecutarse. Durante quizá medio segundo el usuario no tiene ni idea de si la acción que ha llevado a cabo en la interfaz (en este caso seleccionar una categoría de la lista) ha tenido efecto o no. en algunas aplicaciones reales en las que el código es más complicado y puede que haya demoras de e/s debido a accesos a bases de datos, a disco o a redes congestionadas, las esperas para recibir las respuestas a eventos de servidor pueden tardar incluso varios segundos, durante los cuales el usuario no tiene ni idea de qué está pasando. 3. Un efecto muy desagradable de los postback y del que muchos programadores no se percatan es el de las entradas indeseadas en el historial de navegación. en nuestro ejemplo cada vez que seleccionamos un elemento de la lista y se provoca un postback aparece una nueva entrada en la historia del navegador. si el usuario pulsa la flecha para ir a la página anterior irá pasando por cada una de las selecciones que haya hecho en la lista. sin embargo el usuario ha tenido la sensación de estar trabajando todo el tiempo en la misma página. Para él o ella debería ser transparente el hecho de que nosotros por debajo estemos reenviando la página. cuando pulsa el botón de volver a la página anterior lo que un usuario espera es realmente volver a la “pantalla” en la que estuviese previamente, no a los sucesivos pasos de trabajo en la misma página actual. Por supuesto existen excepciones y a veces será necesario todo lo contrario: que incluso en aplicaciones aJaX se creen algunas entradas en el historial. Lo trataremos en otro capítulo con detalle. vamos a retocar nuestro ejemplo sacándole partido a los controles aJaX que se pueden ver en la primera figura. De momento no explicaremos sus funciones y nos limitaremos a añadirlos sin más. en los siguientes epígrafes analizaremos con detalle cada uno de ellos. abre la superficie de diseño de la página asPX y desde el panel de herramientas añade un control ScriptManager que encontrarás en el grupo de extensiones aJaX. asegúrate de que el control se encuentra como primer elemento de la página, es decir, arrástralo delante del primer control DropDownList que teníamos anteriormente. ahora arrastra, justo a continuación del anterior, un control UpdatePanel. al hacerlo verás que aparece una nueva área vacía de poca altura, que es la única pista visual de que se ha arrastrado este último control.

AsP.net AJAX en el servidor 29

selecciona cada uno de las dos listas desplegables y arrástralas dentro del UpdatePanel. asegúrate de que quedan ubicadas dentro de éste. como se observa en la figura, el recuadrado sutil del control UpdatePanel nos permite ver sus límites y saber si los controles se encuentran realmente dentro de él.

Figura 3.- Nuestra aplicación preparada para AJAX con sólo arrastrar dos controles.

Podríamos haber incluido los controles dentro del UpdatePanel también desde el código fuente de la página, en lugar de arrastrándolos, si cambiamos a la vista hTML y nos aseguramos de que sus etiquetas están encerradas dentro de las etiquetas de tipo <contentTemplate> del panel, como se ve en la figura 4.

Figura 4.- las etiquetas de los controles en el código fuente se encuentran dentro del updatePanel.

Ya está. No es necesario hacer nada más que arrastrar este par de controles. ejecuta de nuevo la aplicación. ahora verás que al seleccionar una categoría en la primera lista la segunda se recarga enseguida con los valores apropiados sin recargar la página. el parpadeo ha desaparecido y en la historia del navegador no aparecen nuevas entradas. Más sencillo imposible.

30

tecnologías AsP.net 4.0 (saltando desde la versión 2.0)

Nota:
La mayor parte de la gente se queda con esta idea de sencillez que el control UpdatePanel nos brinda. Si bien es cierto que gracias a él la creación de aplicaciones AJAX se simplifica en gran medida, también es verdad que hay multitud de pequeños detalles a tener en cuenta sobre rendimiento y optimización que se le escapan al que simplemente se queda en la superficie. ello provoca que, luego en producción y sometidas a mucha carga, algunas aplicaciones aJaX ofrezcan muchos problemas de rendimiento, ya que están hechas sin tener un buen conocimiento de lo que se hacía. en este capítulo trataremos de comentar conceptos e ideas clave que nos ayudarán a comprender mejor toda esta tecnología. Por lo de pronto, si ya has leído el capítulo anterior de fundamentos, ya tienes más herramientas que la mayoría de los programadores asP.NeT aJaX que encontrarás por ahí.

2.- PoSTbAckS PArciAlES y rEPiNTAdoS PArciAlES dE PÁgiNA
el proceso que se lleva a cabo por debajo en las páginas aJaX como la del ejemplo anterior se denomina postback con repintado parcial de página. el funcionamiento de una página asP.NeT aJaX no difiere demasiado del de una página asP.NeT normal y corriente. ello es una gran ventaja puesto que no nos obliga a aprender conceptos diferentes o a tratar a las páginas de un modo distinto por el hecho de usar esta tecnología. Lo que ocurre durante un repintado parcial de página (en el que sólo una parte de su interfaz se modifica) es en realidad un postback completo de la misma. La diferencia con una página normal estriba en que éste se realiza de manera asíncrona y “por debajo” usando Javascript para solicitar la página y procesar sus resultados. La otra diferencia es que en lugar del contenido completo se devuelve sólo el hTML que compete a los contenidos del UpdatePanel que se va a actualizar. el código Javascript generado automáticamente por el ScriptManager se encarga de gestionar ese resultado y repintar dinámicamente sólo la parte apropiada de la interfaz, así como actualizar los datos de estado de los controles para subsiguientes recargas de la página. en el servidor todo esto significa que cuando se produce un evento en un control, aunque se vea afectada sólo una pequeña parte de la página aJaX, en realidad se está procesando un postback completo, con todos sus eventos de ciclo de vida. es más, se regenera totalmente el árbol de controles de la página, se lanzan todos los eventos de ciclo de vida de éstos, se envía y recibe todo el viewstate, etc... La única diferencia es que se devuelve sólo el hTML de las partes de la página que se están modificando dentro del UpdatePanel. olvidar esto es un error muy común que cometen los programadores, que no se dan cuenta de que, aunque se vaya a actualizar sólo un pequeño control, en realidad se está enviando el estado de todos los contenidos en la página (y hay ViewStates que ocupan mucho), se ejecutarán todos los eventos de página tales como Page_Load y los eventos de ciclo de vida de todos los controles aunque no intervengan en la acción. o sea, exactamente igual que en una página normal.

AsP.net AJAX en el servidor 31

esto tiene mucha importancia ya que podemos tener una página enorme que se construye a partir de un proceso complejo y costoso (por ejemplo obtener mucha información de una base de datos o un servicio Web remoto), en la que hemos añadido un UpdatePanel en el que se modificará solamente una pequeña cantidad de información como respuesta a una acción. Podemos pensar que el refresco de ese panel será una operación ágil y muy poco costosa ya que los datos recibidos son minúsculos. es muy fácil que nos equivoquemos y la actualización parcial aparentemente inocente lleve mucho tiempo y sea muy costosa en términos de red y de proceso en el servidor si no hemos sido cuidadosos en nuestro código. el nombre que se le suele otorgar a los postback de páginas aJaX tampoco ayuda demasiado a aclarar este concepto, ya que normalmente se les denomina en artículos y documentaciones como “postback parciales”, para distinguirlos de los “postback normales” que se ejecutan cuando no hay funcionalidad aJaX. en mi opinión deberían llamarse “postback asíncronos” o, directamente, no distinguir entre unos y otros y sólo hablar de actualizaciones parciales, que es lo que realmente ocurre. No te dejes engañar por el nombre cuando leas esta denominación y recuerda que los postback son siempre completos. La primera consecuencia de esto es que, aunque parezca que no lo necesitamos, debemos seguir comprobando en la carga de la página si nos encontramos en un postback o no, para lo cual usaremos la propiedad IsPostBack de la página. así evitaremos operaciones de inicialización innecesarias en cada recarga, como haríamos en cualquier página normal. si por el motivo que sea necesitamos saber si nos encontramos dentro de un postback conducente a una actualización parcial de la página, o sea de un postback asíncrono de aJaX, podemos saberlo consultando la propiedad IsAsyncPostback del control ScriptManager, que tomará el valor True cuando este sea el caso.

3.- El coNTrol ScriPTMANAgEr
el núcleo sobre el que se sustenta toda la funcionalidad aJaX de asP.NeT es el control ScriptManager. como sabemos, la funcionalidad aJaX de cualquier aplicación Web (sea con .NeT o no) se basa en la interacción entre el navegador y el servidor, la cual se gestiona a través de código Javascript que realiza llamadas a recursos remotos y maneja las respuestas obtenidas para actualizar la interfaz de usuario. el control scriptManager se encarga de generar todo el código Javascript necesario para sustentar la funcionalidad aJaX en el navegador. como veremos al analizar la parte cliente de aJaX, este código Javascript se ofrece en forma de librerías especializadas que habilitan todas las características especiales de aJaX mencionadas al principio del capítulo: llamadas a servicios Web, funciones de comunicaciones... y por supuesto, como funcionalidad estrella, la capacidad de efectuar postbacks en segundo plano y hacer el repintado parcial de las páginas, que es lo que acabamos de experimentar en el ejemplo.

32

tecnologías AsP.net 4.0 (saltando desde la versión 2.0)

siempre que deseemos utilizar funcionalidad aJaX debe existir un control ScriptManager en la misma. También se pueden utilizar dentro de controles de usuario y en Master Pages, aunque luego veremos las particularidades de usarlos ahí. entre las funciones de este control están: • • • • • • Generar los scripts necesarios para sustentar la funcionalidad aJaX y las extensiones de Javascript para desarrollo en el lado cliente. registrar nuestros propios scripts en el cliente y el envío de estos dinámicamente durante los repintados de página. Proporcionar acceso a servicios Web en el servidor. soporte para localización y globalización de código de lado cliente en función de los ajustes culturales del navegador. Dar soporte a los servicios de Membership, Roles y Profile de asP.NeT para hacer uso de estos directamente desde el lado cliente, con Javascript. Permitir la creación de controles y comportamientos basados en Javascript (extensiones) que otorgan de funcionalidad extra a los controles hTML normales del navegador y a los controles Web de asP.NeT.

Dado que, como vemos, este control tiene muchas funcionalidades aparte de la de permitir el repintado parcial de página, si no queremos utilizar esta característica y sólo estamos interesados en hacer otras cosas con él en el lado cliente, podemos desactivarla estableciendo a False su propiedad EnablePartialRendering. Por otro lado es posible que en navegadores muy antiguos la funcionalidad de repintado parcial no esté soportada. en esos casos el control ScriptManager se degrada elegantemente y deja que la página se comporte de la manera habitual, con postbacks síncronos y repintado completo de la página. Podemos determinar esta situación consultando su propiedad SupportsPartialRendering.

4.- El coNTrol uPdATEPANEl
este control es una abstracción que nos permite indicar qué partes de nuestra interfaz de usuario queremos que tengan la capacidad de actualizarse parcialmente, de forma independiente al resto de la página. como hemos visto ya (figura 4) el marcado del control tiene una pareja de etiquetas <ContentTemplate> que definen una zona contenedora de otros controles. Todos los controles definidos dentro de esta zona pueden participar de un repintado parcial de la página. esta región comprendida entre ambas etiquetas se corresponde en el diseñador visual de visual studio con el área contenedora del UpdatePanel a la que podemos arrastrar los controles. además de añadir controles en esta zona visualmente o incluyendo sus etiquetas en el marcado de la página, es posible añadirlos (o retirarlos) también dinámicamente

AsP.net AJAX en el servidor 33

mediante código. De esta forma se pueden variar en tiempo de ejecución los contenidos de la zona de repintado parcial. Para ello se debe manipular la colección Controls de la propiedad ContentTemplateContainer del UpdatePanel, así por ejemplo:
Dim Contenedor As Control = UpdatePanel1. ContentTemplateContainer Dim lbl As New Label() lbl.Text = DateTime.Now.ToString(“hh:mm:ss”) Contenedor.Controls.Add(lbl)

5.- ModoS dE AcTuAliZAcióN PArciAl
en una página podemos tener tantos UpdatePanel como necesitemos, para actualizarlos parcialmente de forma individual, coordinada o todos a la vez. Un control UpdatePanel puede contener a su vez a otros UpdatePanel, generándose una jerarquía de actualizaciones parciales que sigue determinadas reglas. Lo más importante a tener en cuenta durante un repintado parcial es qué lo provoca y qué efectos tendrá sobre los diferentes UpdatePanel que tenemos repartidos por la página. cuando un UpdatePanel se encuentra dentro de otro, el del interior se actualiza siempre que lo hace su contenedor. cuando el UpdatePanel es independiente (la situación más habitual) su comportamiento durante el repintado parcial de la página viene determinado por el valor de su propiedad UpdateMode, que puede tomar los siguientes valores: • Always: si la propiedad UpdateMode tiene este valor el contenido se refrescará después de cualquier postback que se realice a la página, sea éste generado específicamente para este control o no. esto es importante ya que este es el valor por defecto de la propiedad. si no somos cuidadosos y tenemos varios UpdatePanel en la página veremos que, en muchas ocasiones, se actualizan zonas que no contábamos que se iban a refrescar, ya que estábamos actuando realmente en otro lugar. Conditional: el contenido del panel sólo se repintará si el control que lanza el postback es uno de los disparadores para el UpdatePanel (ahora veremos qué es esto) o bien cuando llamamos explícitamente al método Update del panel desde un evento de servidor. Por supuesto, como ya se ha comentado, se repintará también siempre que esté contenido dentro de otro panel que se ha repintado.

Jugando con esta propiedad podemos obtener un gran control sobre en qué momento y ante qué eventos se refrescará el contenido de una zona concreta de la página.

34

tecnologías AsP.net 4.0 (saltando desde la versión 2.0)

6.- diSPArAdorES
De manera predeterminada los controles que están dentro de los UpdatePanel provocarán un repintado parcial de la página, mientras que los controles que se encuentran fuera de éstos provocarán postbacks normales con la recarga completa de la misma. aunque este comportamiento pueda resultar adecuado para muchos casos, normalmente vamos a necesitar un mayor control sobre las situaciones que provocan la recarga de las páginas y qué zonas de ésta se ven afectadas. Por ejemplo, si tenemos un control que queremos que provoque el refresco parcial de una parte de la página alejada físicamente de donde está éste ubicado, tenemos dos opciones: 1. Incluir el control y la zona que queremos actualizar dentro del mismo UpdatePanel. esto probablemente nos obligue a incluir muchos otros controles en el panel, que no nos interesa en absoluto que participen en un repintado parcial, puesto que no van a cambiar o queremos que se vean afectados por otros eventos diferentes. Llevado al extremo algunos programadores con poco conocimiento, lo que hacen es ¡rodear con un UpdatePanel todos los controles de su página!. esto es obviamente una solución muy ineficiente y como se suele decir coloquialmente es “matar moscas a cañonazos”. 2. Definir qué controles actuarán como disparadores de cada panel, y por lo tanto lanzarán postbacks asíncronos conducentes a refrescar el contenido del mismo. estos controles no tienen porqué estar dentro de un UpdatePanel, sino en cualquier lugar de la página. esta es la solución inteligente y optimizada. Para implementar esta funcionalidad se definen los disparadores o Triggers. Por defecto todos los controles contenidos dentro de un UpdatePanel se comportan como disparadores de éste. es por ello que en el ejemplo anterior no hemos tenido que hacer nada para que funcionara el repintado parcial de la segunda lista desplegable. este comportamiento se controla a través de la propiedad ChildrenAsTriggers del control UpdatePanel. si la establecemos como False entonces los controles que contiene no serán disparadores y tendremos que definirlos a mano. el uso más común de los disparadores es el de asociar eventos de servidor de controles externos a un panel para provocar repintados parciales de éste. visual studio nos brinda un diálogo especial que facilita su definición. vamos a verlo en funcionamiento con un ejemplo. añade al proyecto de prueba una nueva página “Triggers.aspx”. arrastra sobre ella un ScriptManager y un UpdatePanel. Dentro de este último coloca una etiqueta. ahora, fuera del panel, justo debajo, inserta un botón. el aspecto final debería ser como el de la figura 5:

AsP.net AJAX en el servidor 35

Figura 5.- Aspecto de nuestra aplicación de prueba

en el manejador del evento de pulsación del botón simplemente vamos a mostrar la hora actual dentro de la etiqueta:
Label1.Text = DateTime.Now.ToString(“HH:MM:ss”)

ahora ejecuta la página. al pulsar sobre el botón cabría esperar que la etiqueta mostrase la hora sin necesidad de recargar la página. sin embargo comprobarás que en lugar de eso se provoca un postback y un refresco de toda la página. el motivo es que el botón, al estar fuera del UpdatePanel no es automáticamente un disparador para su repintado parcial. vamos a ponerle remedio. selecciona el control UpdatePanel y en sus propiedades (pulsa F4) verás que existe una colección Triggers.

Figura 6.- colección de disparadores del panel

36

tecnologías AsP.net 4.0 (saltando desde la versión 2.0)

Utilizando el botón con tres puntos situado a la derecha se abre el diálogo de gestión de disparadores, que podemos ver en la figura 7.

Figura 7.- Editor de disparadores de un panel

el botón de añadir nos permite asignar uno o varios controles como disparadores del panel, es decir, como controles que van a provocar un repintado parcial del mismo. La rejilla de propiedades de la derecha nos facilita la selección de controles y eventos. en nuestro ejemplo seleccionaremos como control disparador el botón de actualizar la hora (cmdHora), y dentro de los posibles eventos de éste, el evento Click, es decir su pulsación. se podría hacer que el disparador fuese cualquier otro evento, e incluso que varios eventos de un mismo control sirvan como disparadores del panel. Una vez añadido nuestro botón como disparador, acepta para cerrar el diálogo y vuelve a ejecutar la aplicación. comprobarás como, ahora sí, al pulsar el botón la hora se visualiza en la etiqueta sin necesidad de refrescar la página por completo. si nos fijamos en el código de marcado de la página, veremos que es posible definir los disparadores del panel manualmente de forma sencilla, usando la etiqueta <Triggers>:
<asp:UpdatePanel ID=”UpdatePanel1” runat=”server”> <ContentTemplate> <asp:Label ID=”Label1” runat=”server”></asp:Label>

AsP.net AJAX en el servidor 37

</ContentTemplate> <Triggers> <asp:AsyncPostBackTrigger ControlID=”cmdHora” EventName=”Click” /> </Triggers> </asp:UpdatePanel>

Fíjate en que lo único que necesitamos es establecer las dos propiedades del disparador como unos simples atributos de texto, indicando el nombre del control y el del evento a asociar. Los disparadores asíncronos como este se pueden utilizar también para lanzar actualizaciones parciales a partir de controles de dentro del panel cuando ChildrenAsTriggers es falso. También para forzar la actualización de un panel padre desde un control ubicado en un panel anidado dentro de él. Por defecto los controles que están dentro de los UpdatePanel harán un refresco parcial asíncrono de la página. en ocasiones sin embargo es posible que necesitemos que, si bien la mayoría de los controles del panel se comporten así, uno o varios de ellos provoquen un refresco completo de la página. Por ejemplo cuando un cambio dentro del panel afecta a datos que se están visualizando fuera del mismo. Para ello existen un tipo especial de disparadores que se llaman simplemente PostBackTriggers. si te fijas bien en la figura y en el listado anteriores verás que el disparador que hemos utilizado es de tipo AsyncPostBackTrigger porque desencadena un postback asíncrono. en la figura 7 se ve como al desplegar el botón de añadir disparador hay un tipo adicional sin el prefijo Async, siendo simplemente PostbackTrigger. este tipo de disparadores te dejan seleccionar un control contenido en el UpdatePanel, y al asociarlo conseguiremos generar un postback con recarga completa de la página aunque la propiedad ChildrenAsTriggers tenga el valor por defecto de True.

7.- iNdicAcióN dE ProgrESo dE lAS llAMAdAS ASíNcroNAS
en los ejemplos que hemos hecho, las tareas realizadas como respuesta a los eventos de servidor eran triviales para poder centrarnos en la funcionalidad aJaX únicamente. en una aplicación real tendremos tareas de todo tipo que pueden ser más o menos costosas. Por ejemplo ciertas labores de acceso a datos pueden tardar bastante en realizarse, y si le sumamos la velocidad de la red que puede ser baja, una carga importante de usuarios en el servidor, etc... es posible —y hasta frecuente— que algunas de las llamadas asíncronas que se realizan al servidor tarden más de la cuenta en regresar. Mientras el navegador espera la respuesta de una llamada asíncrona para repintado parcial, el usuario no tiene pista alguna de lo que está pasando. en cuanto pase medio segundo y no haya un efecto visible en la página la mayoría de los usuarios

38

tecnologías AsP.net 4.0 (saltando desde la versión 2.0)

se empezarán a preguntar si ha fallado algo. Incluso muchos se pondrán a pulsar los botones nuevamente varias veces para comprobar si responde, agravando el problema pues estarán lanzando más peticiones al servidor. Por eso, salvo en los casos más triviales, es importante utilizar algún tipo de indicador visual para los usuarios que aparezca en pantalla mientras dura la actualización (el viaje petición-respuesta-procesamiento desde el navegador al servidor y vuelta otra vez). Pero tampoco es recomendable que aparezca inmediatamente pues si la llamada es muy rápida se verá casi como un parpadeo que también desconcentrará al usuario. Para evitarnos la tarea de tener que programar algo así por nosotros mismos, asP.NeT aJaX ofrece un control llamado UpdateProgress que sirve precisamente para eso. Puedes verlo en la lista de controles de la figura 1. al arrastrarlo sobre la superficie de nuestra página asPX se mostrará como un control contenedor, de forma que podremos colocar dentro de él los elementos que deseemos que aparezcan durante una actualización asíncrona de la página. aunque hay quien prefiere un espartano cartel de texto (mira sino los indicadores de progreso que muestra GMail, que son unas simples letras sobre fondo granate), lo más habitual es usar un gráfico animado en formato GIF que muestre una sensación de progreso mientras carga la página por debajo. en la página www.ajaxload.info dispones de una interesante utilidad gratuita que te permite generar online muchos gráficos de progreso diferentes. simplemente escoges el tipo de gráfico entre una amplia lista, los colores y si quieres que sea transparente o no y el programa genera el gráfico para ti y te permite descargarlo.

Figura 8.- utilidad online para generar gráficos de progreso AJAX

AsP.net AJAX en el servidor 39

Para probar su funcionamiento vamos a añadir uno de estos controles a la página, justo a continuación del ScriptManager. Genera en la ajaxload.info un gráfico que te guste para indicar el progreso, grábalo a disco en el proyecto, refresca el explorador de soluciones y desde él arrástralo dentro del control de progreso. selecciona el control UpdateProgress y ve a sus propiedades. establece la propiedad AssociatedUpdatePanelID con el nombre del UpdatePanel, y su propiedad DynamicLayout como False, tal y como se ve en la figura 9.

Figura 9.- Propiedades del control updateProgress

con estas propiedades lo que conseguiremos es que el indicador de progreso se muestre automáticamente durante las actualizaciones parciales del UpdatePanel1, y que el espacio que ocupa en la página se conserve incluso cuando está oculto. si establecemos DinamicLayout como True, que es su valor predeterminado, cuando el indicador aparezca reclamará su sitio en la página (ya que antes no ocupaba nada), desplazando al resto de los controles hacia abajo, lo que puede crear un efecto feo si no lo tenemos en cuenta. con el valor que le hemos dado, el espacio que ocupa está reservado y al aparecer no provoca desplazamiento alguno. La representación en marcado del control UpdateProgress es como la siguiente:
<asp:UpdateProgress ID=”UpdateProgress1” runat=”server” AssociatedUpd atePanelID=”UpdatePanel1”> <ProgressTemplate> <img alt=”Cargando...” src=”circulito.gif” style=”width: 16px; height: 16px” /> </ProgressTemplate> </asp:UpdateProgress>

Lo que está dentro de sus etiquetas <ProgressTemplate> (en la superficie contenedora si lo vemos en modo diseño) se convierte en tiempo de ejecución en el hTML correspondiente encerrado dentro de una capa con la etiqueta <div>.

40

tecnologías AsP.net 4.0 (saltando desde la versión 2.0)

Todavía queda por ver una propiedad muy importante: DisplayAfter. en ella indicaremos el tiempo en milisegundos que tardará el indicador en aparecer si la operación asíncrona no ha terminado. Por omisión este tiempo es de 500 milisegundos. así, si la operación de postback tarda más de medio segundo en finalizar, aparecerá el indicador automáticamente para que el usuario sepa que el proceso está funcionando y que tardará un poco. al terminar la operación se ocultará también de manera automática. Para probar el indicador en nuestro ejemplo sólo nos resta simular el efecto de una operación lenta en el servidor. así que en el evento de pulsación del botón añadiremos una línea que hará que se bloquee la ejecución durante dos segundos justo antes de actualizar la hora en la etiqueta:
System.Threading.Thread.Sleep(2000)

ahora ejecuta la aplicación y pulsa el botón para mostrar la hora. ¡eh! ¿Qué pasa? No se está mostrando el indicador de progreso. La cosa no funciona... el motivo es que en este caso nuestro disparador era un elemento externo al UpdatePanel (el botón está fuera y lo asociamos con un Trigger, si recuerdas). cuando el control de progreso se asocia a un UpdatePanel concreto mediante su propiedad AssociateUpdatePanelID, no es capaz de detectar el comienzo y finalización de los postback generados por disparadores externos al panel. ello se debe a que el Javascript asociado lo que hace es recorrer la jerarquía de elementos DoM que hay bajo el UpdatePanel que debe controlar, para encontrar qué control ha lanzado el repintado. como un disparador externo no está en esa jerarquía no es capaz de encontrarlo y por lo tanto el progreso no se muestra. No tendríamos problema si el botón estuviese dentro del panel. Para solucionarlo hay una acción muy simple que podemos llevar a cabo: eliminar la propiedad AssociatedUpdatePanelID. si tenemos en la página un control de indicación de progreso que no está asociado a un panel, lo que conseguimos es un indicador de progreso universal que se mostrará para cualquier actualización parcial que se haga en la página. si ahora ejecutas de nuevo la página verás que ya se comporta como era de esperar y el gráfico animado de progreso aparece al cabo de medio segundo, ocultándose automáticamente al final. De hecho es muy habitual tener un único control de indicación de progreso en la página que sirva para el proceso de repintado de cualquiera de los paneles contenidos en ésta.

8.- rEFrEScoS PArciAlES PEriódicoS
otro de los controles de aJaX disponibles es el control Timer. Éste nos permite lanzar automáticamente procesos de actualización parcial con una frecuencia periódica y sin que sea necesaria acción alguna por parte de un usuario. se puede utilizar para actualizar los contenidos de un UpdatePanel o por el contrario actualizar la página completa. otra aplicación interesante es la de ejecutar

AsP.net AJAX en el servidor 41

periódicamente código en el servidor aunque no sirva para actualizar nada visible en la interfaz. es un control dependiente de aJaX y aunque se use para esto último va a requerir siempre la presencia de un ScriptManager en la página (tampoco es la mejor forma de hacer algo así, todo hay que decirlo). La única propiedad interesante de este control es Interval. a través de ella se establece el número de milisegundos que servirán como periodicidad del temporizador. Por defecto el periodo es de un minuto (60.000 segundos). No conviene usar un valor demasiado bajo para Interval ya que correremos el riesgo de saturar el servidor con el número de peticiones muy seguidas que se generarán. cada vez que se cumple el tiempo indicado en el intervalo se lanza un evento de servidor para el temporizador. este evento se denomina Tick. como cualquier otro evento de servidor (como el clic de un botón o cualquier otro), éste provocará un postback de la página. Y al igual que con cualquier otro control, dependiendo de donde esté colocado dentro de la página se producirá un refresco parcial o total de la misma. así, si el temporizador está dentro de un UpdatePanel con ChildrenAsTriggers= True (valor por defecto y el más habitual), el resultado será que se refrescan los contenidos del panel asíncronamente con la periodicidad especificada. si lo colocamos fuera de un UpdatePanel se producirá un refresco completo de la página. Por supuesto podemos asignarlo como un disparador para uno o varios UpdatePanel, en cuyo caso lo situaremos fuera y nos servirá para hacer que se refresquen parcialmente cada cierto tiempo. si tenemos varios temporizadores en la página y todos tienen el mismo intervalo es mucho más eficiente colocar uno sólo, fuera de todos los paneles, y asignarlo como disparador para refrescarlos a todos. otra cosa a tener en cuenta es que para actuar sobre los controles de la página durante el evento del servidor, es indistinto hacerlo en el evento Tick del control o bien directamente en el evento Load de la página, ya que como sabemos durante el postback (aunque sea asíncrono) la página ejecuta su ciclo de vida normal. en el código descargable he incluido una página de ejemplo de uso del temporizador que se limita a actualizar la hora en una etiqueta cada segundo, consiguiendo un reloj en funcionamiento. es evidente que algo tan sencillo es muy fácil de conseguir sin más que un poco de Javascript en el cliente, así que hacerlo con un temporizador es un verdadero derroche de recursos y, como decía antes, “matar moscas a cañonazos” :-), pero nos sirve para ilustrar su funcionalidad. sólo hay una última cosa importante que debemos tener en cuenta si la precisión del intervalo es importante para nosotros, y es que los tiempos pueden cambiar ligeramente en función de si el temporizador está dentro de un UpdatePanel o fuera de él. si el Timer está fuera de cualquier UpdatePanel actuando como disparador de alguno de ellos, su periodo de refresco se recarga automáticamente en cuanto lanza el evento de servidor, sin esperar a que éste regrese, ya que el código de la página que le afecta no se modifica. sin embargo cuando lo colocamos dentro de un panel, al refrescar su contenido, se regenera el código del temporizador y el tiempo comienza a contar de nuevo. esto ocurre tras haber regresado del postback asíncrono. Por lo tanto hay una diferencia con el intervalo de la otra situación que será tanto mayor cuanto más tarde en procesarse la llamada asíncrona al servidor.

42

tecnologías AsP.net 4.0 (saltando desde la versión 2.0)

Debes tenerlo en cuenta porque es una sutil diferencia pero puede ser importante en algunas aplicaciones.

9.- uNicidAd dEl ScriPTMANAgEr
al trabajar con los controles de servidor de asP.NeT aJaX hay que tener en cuenta una cosa muy importante, y es que cuando digo que debe existir un control ScriptManager en la página, me estoy refiriendo a que debe haber uno y sólo uno en todo el árbol de controles de la misma. si intentamos añadir más de un control de este tipo a la página el compilador no se quejará, pero recibiremos un error en tiempo de ejecución como el de la figura.

Figura 10.- No es posible tener más de un control ScriptManager en una página

esto tiene más importancia de la que parece a primera vista y es que afectará a la forma en la que podremos reutilizar algunas partes de nuestra aplicación. Por ejemplo, si tenemos una o varias Master Pages deberemos decidir si el control ScriptManager se colocará en la MP o bien tendremos que colocarlo en cada una de las páginas que hagan uso de funcionalidad aJaX. Lo que no podemos es tenerlo en los dos sitios a la vez ya que en tiempo de ejecución, al fusionarse los controles de la página plantilla y de la página funcional, nos encontraremos con dos controles y se producirá un error. Lo mismo ocurre si encapsulamos funcionalidad aJaX dentro de un control de usuario y en éste incluimos el ScriptManager. si luego lo arrastramos sobre una página que ya tenga su propio ScriptManager se producirá un conflicto y la página no funcionará. en general si nuestra aplicación hace un uso amplio de las funcionalidades aJaX, como es cada vez más habitual, lo más recomendable es incluir el ScriptManager directamente en la plantilla (Master Page). así estará disponible para todas las páginas hija que hagan uso de la misma. si alguna de las páginas hija no utiliza refrescos parciales siempre podemos desactivar esta característica obteniendo una referencia al control durante la carga y usando su propiedad EnablePartialRendering, que ya hemos visto:

AsP.net AJAX en el servidor 43

Dim sm As ScriptManager = ScriptManager.GetCurrent() sm.EnablePartialRendering = false

el método estático GetCurrent de la clase ScriptManager nos permite obtener una referencia a la instancia actual que se esté usando en la página, independientemente de que esté ubicada en la propia página, en un control o en la Master Page.

10.- El coNTrol ScriPTMANAgErProXy
como hemos comentado (y veremos con detalle luego) el ScritpManager ofrece soporte para multitud de cuestiones más allá del repintado parcial de las páginas. si incluimos uno de estos controles en una Master Page y luego requerimos servicios adicionales del mismo en una de sus páginas hijas, tenemos un problema. si por ejemplo, queremos añadir una referencia a un script o a un servicio Web que es de uso específico de la página actual, dado que no podemos incluir otro control ScriptManager para hacerlo ¿cómo procederemos?. Una posible opción sería incluirlo en el que está en la Master Page, pero así tendríamos que incluir todos los posibles servicios en la MP y ponerlos a disposición de todas las páginas, los necesiten o no, lo cual es muy ineficiente y propenso a errores. Para solucionar este problema existe el control ScriptManagerProxy. este control, disponible también en el panel de herramientas (ver figura 1), se arrastra a la página como otro cualquiera y actúa como un apéndice del script Manager único que está en la Master Page para añadirle las características adicionales que necesitemos. Por ejemplo, este fragmento muestra la sintaxis de uno de estos controles que se usa para incluir en la página, en la sección <scripts> (luego la estudiaremos), un código de script específico que se necesita para dotarla de alguna funcionalidad de lado cliente:
<asp:ScriptManagerProxy ID=”ScriptManagerProxy1” runat=”server”> <Scripts> <asp:ScriptReference Path=”scriptEspecifico.js” /> </Scripts> </asp:ScriptManagerProxy>

este tipo de proxy se puede utilizar también en controles de usuario que luego van a ser utilizados en páginas que ya tiene un ScriptManager, bien porque lo incluyan directamente, bien porque lo tenga la Master Page correspondiente. Debes recordar bien este control porque lo usarás mucho más de lo que te imaginas en cualquier aplicación grande.

11.- gESTióN dE ErrorES AJAX
en condiciones normales todo va a funcionar de maravilla. Pero la dura realidad es que siempre, en algún momento, pasa algo que hace que las cosas fallen. si durante un postback asíncrono se produce un error en el servidor ¿cómo podemos gestionarlo?.

44

tecnologías AsP.net 4.0 (saltando desde la versión 2.0)

vamos a crear una pequeña aplicación de ejemplo que produzca errores para ver cómo podemos gestionarlos. crea una nueva página en el proyecto, “erroresajax.aspx”, y añádele un ScriptManager, un UpdatePanel y dentro de éste un simple botón con el texto “Provocar error”. en el manejador del evento click de este botón lanza una excepción así:
Throw New Exception(“Error provocado a mano!!”)

ahora ejecútala pulsando cTrL + F5 para evitar que se trabaje en modo depuración, pues en ese caso saltaría un punto de interrupción en visual studio y no verías cómo funciona en la realidad, cuando esté en producción. verás que el error en el lado servidor se traduce en el navegador en forma de un error de Javascript. si tienes la depuración de Javascript activada verás un mensaje como el de la figura 11. como se puede comprobar en dicha figura, el mensaje descriptivo del error original se visualiza en el error de Javascript también.

Figura 11.- Error de JavaScript provocado en el servidor

en este caso el error, al ser manual, tiene una descripción amigable y neutra, y no pasa nada porque se vea. sin embargo esta situación no es la más recomendable. Una de las reglas básicas de seguridad de aplicaciones dice que no se debe dar información técnica de errores a usuarios finales. Generalmente los errores que se producirán en el servidor incluyen en su descripción detalles internos de la aplicación, por ejemplo nombres de campos en bases de datos o rutas en disco, que podrían dar información valiosa a posibles atacantes. Por este motivo se recomienda gestionar siempre los errores y devolver al usuario un mensaje más amigable, sin dar detalles internos. Para ello el control ScriptManager nos ofrece el evento AsyncPostBackError, en el que podremos gestionar cualquier tipo de excepción que se produzca durante un postback asíncrono.

AsP.net AJAX en el servidor 45

Figura 12.- Evento para gestión de errores en postbacks asíncronos.

como segundo argumento de este evento tenemos un objeto de tipo AsyncPostbackErrorEventArgs. su única propiedad interesante, Exception, nos permite acceder a la excepción producida y trabajar con ella para actuar en consecuencia. en nuestro ejemplo vamos simplemente a poner un mensaje más amigable independientemente de cuál sea la excepción producida:
Protected Sub ScriptManager1_AsyncPostBackError(ByVal sender As Object, ByVal e As System.Web.UI.AsyncPostBackErrorEventArgs) Handles ScriptManager1.AsyncPostBackError ScriptManager1.AsyncPostBackErrorMessage = “Se ha producido un error en la operación. Si persiste contacte con el administrador” End Sub

Utilizamos la propiedad AsyncPostBackErrorMessage del ScriptManager para asignar un mensaje más amigable:

Figura 13.- El mensaje amigable en el error de lado cliente

No obstante, sería más adecuado que se mostrase de una forma más integrada dentro de la página, y no traducido a una excepción de Javascript.

46

tecnologías AsP.net 4.0 (saltando desde la versión 2.0)

La parte cliente de asP.NeT aJaX ofrece soporte para interceptar de manera sencilla los distintos eventos en el ciclo de vida de las peticiones asíncronas. vamos a sacarles partido para detectar desde Javascript que se ha producido un error durante un evento de servidor asíncrono, y mostrar al usuario un mensaje dentro de la propia página, mediante capas. en la siguiente figura se puede observar el aspecto del mismo.

Figura 14.- Mensaje de error personalizado

se muestra el mensaje de error dentro de una pequeña capa flotante, y se oscurece la página para darle más protagonismo al mensaje, e impedir al mismo tiempo que el usuario pueda pulsar sobre el resto de los elementos. es una forma sencilla de simular un diálogo modal dentro de una página usando únicamente capas y estilos. el efecto de transparencia se consigue con un gráfico GIF semitransparente puesto como fondo de una capa. Para conseguirlo se debe incluir en la página un poco de código Javascript que se encargue de interceptar un posible error al volver del postback asíncrono, y que luego se encargue de mostrar y ocultar la capa flotante sobre la que se mostrará el mensaje.

AsP.net AJAX en el servidor 47

Nota:
esta parte es código de lado cliente, que forma parte de otro capítulo del libro, pero lo he incluido aquí porque está directamente relacionado con la gestión de errores. No vamos a ver los detalles del código Javascript utilizado para conseguir toda la funcionalidad. Nos centraremos únicamente en la parte relacionada con la gestión del error, que es la que nos ocupa. Puedes ver el código completo del ejemplo en el ZIP con las demos del libro.

La clase PageRequestManager proporcionada en el lado cliente por asP.NeT aJaX sirve para acceder a toda la funcionalidad de postbacks asíncronos. Podemos obtener una referencia a la clase actualmente utilizada para realizar las peticiones con esta expresión Javascript:
Sys.WebForms.PageRequestManager.getInstance()

el método add_endRequest() de esta clase sirve para añadir nuevos manejadores al evento de finalización de llamada asíncrona. es decir, que podemos ser notificados automáticamente de cuando un postback asíncrono regresa. si definimos una función Javascript con el nombre de, por ejemplo, EndRequesthandler, podemos usarla para interceptar el evento y así poder trabajar con los datos de la petición:
Sys.WebForms.PageRequestManager.getInstance().add_ endRequest(EndRequestHandler);

La función recibe dos parámetros, al más puro estilo .NeT aún siendo Javascript, siendo el primero de ellos el “sender” y el segundo una referencia a un objeto de la clase EndRequestEventArgs. esta clase sólo tiene dos propiedades interesantes: • Error: contiene información del error. como Javascript no tiene propiedades, por convención se debe leer su contenido llamando al método get_error(). La clase error devuelta tiene tres propiedades (message, number y name) que nos permiten averiguar respectivamente el mensaje, el número y el nombre del error. errorHandled: se usa para indicar a la infraestructura de aJaX que el error ya lo hemos gestionado nosotros y que por lo tanto no debe dejar que se produzca. se escribe en ella, por convención, llamando al método: set_errorHandled().

en el código de ejemplo descargable están todos los detalles comentados, pero basta indicar que lo que se hace es comprobar en un condicional si la propiedad Error contiene o no una referencia válida, en cuyo caso se muestra el mensaje de la figura anterior, y se indica que ya hemos gestionado nosotros el problema, para evitar que se manifieste como una excepción de Javascript. Descárgate y échale un vistazo al código de ejemplo.

48

tecnologías AsP.net 4.0 (saltando desde la versión 2.0)

en aplicaciones que no usan aJaX lo habitual es que gestionemos los errores usando alguna página especial, que configuramos en nuestro web.config para que la infraestructura de asP.NeT se encargue de mostrarla. Por ejemplo, podríamos tener esta configuración:
<customErrors mode=”RemoteOnly” defaultRedirect=”errorGenerico.aspx”> <error statusCode=”500” redirect=”ErrorServidor.aspx” /> <error statusCode=”403” redirect=”Prohibido.aspx” /> </customErrors> <error statusCode=”404” redirect=”NoEncontrado.aspx” />

así, cuando se produzca un error en el servidor (código de estatus hTTP igual a 500) se mostrará automáticamente al usuario la página “errorservidor.aspx”. De similar manera se comportaría la aplicación ante intentos de acceso no autorizados (403) o páginas no encontradas (404), redirigiendo a las páginas correspondientes. si se produce cualquier otra circunstancia no contemplada específicamente se muestra una página de error genérica “errorGenerico.aspx”. Por defecto el ScriptManager trata los errores producidos en el servidor para que sean totalmente compatibles con el comportamiento indicado en el web.config. Por lo tanto si hemos definido una página especial para errores de servidor en nuestra configuración y se produce una excepción durante un postback asíncrono, la infraestructura de asP.NeT aJaX se encarga de que todo funciones de la manera normal, redirigiendo a la página indicada. Debemos tener en cuenta pues, que en caso de haber configurado páginas de error personalizadas, no recibiremos jamás en el cliente notificaciones de los errores, y las técnicas mostradas anteriormente no funcionarán. La propiedad AllowCustomErrorsRedirect del ScriptManager sirve para ajustar este comportamiento. Por defecto vale True y obtenemos el funcionamiento habitual. si la establecemos a False se hará caso omiso del web.config para los errores producidos durante repintados parciales. en el ejemplo descargable de la Web puedes quitar los comentarios en el web.config para habilitar una página personalizada para los errores 500. verás cómo a partir de ese momento el diálogo de la figura 14 se deja de mostrar y aparece la página de error que se ha definido.

12.- iNcoMPATibilidAdES dE AJAX
a la hora de usar el repintado parcial de los controles y las llamadas asíncronas debemos tener en cuenta algunas limitaciones. son pocas pero pueden impactar negativamente en nuestros desarrollos si no las hemos considerado de antemano.

AsP.net AJAX en el servidor 49

algunos controles Web de asP.NeT, dado su modo de trabajar, no son compatibles con las actualizaciones parciales de la página y, por consiguiente, no están diseñados para funcionar dentro de un control UpdatePanel. La mayoría no ofrecen problemas, pero hay algunos que presentan incompatibilidades que, o bien impiden su funcionamiento o limitan alguna de sus características: • Los controles de envío de archivos al servidor: FileUpload y HtmlInputFile, que sólo funcionarán de modo nativo si se realiza un postback síncrono común. en el mercado hay algunos controles de publicación de archivos que soportan asincronismo (por ejemplo el de Subgurim, gratuito y descargable desde http://fileuploadajax.subgurim.net), si bien no usan UpdatePanel para conseguirlo. Los controles TreeView y Menu. el control Substitution. Los controles Login, PasswordRecovery, ChangePassword y CreateUserWizard cuyo contenido no ha sido convertido previamente en plantillas editables. es una limitación con poco impacto porque, o bien se convierten a plantillas o bien se pueden utilizar las aPI de seguridad directamente desde Javascript para conseguir el mismo efecto, como veremos en el próximo capítulo. Los controles GridView y DetailsView cuando su propiedad EnableSortingAndPagingCallbacks es True. Por defecto no tienen activada esta propiedad por lo que no hay problema. además, esta propiedad se utiliza precisamente para conseguir el mismo efecto que con el UpdatePanel (sólo que utiliza otras técnicas diferentes), por lo que no hay problema alguno en desactivarla dentro del panel. No se puede considerar una verdadera limitación.

• • •

adicionalmente si utilizas visual studio 2005 con asP.NeT 2.0, en esta versión antigua del entorno de desarrollo y del Framework no están soportados en aJaX los controles relacionados con WebParts. Tampoco funcionarán los controles de validación, si bien se pueden hacer compatibles con sólo desconectar la validación de lado cliente (propiedad EnableClientScript = False). No existen estos problemas de compatibilidad con estos controles en versiones de visual studio superiores a la 2008, incluida ésta. Todos los demás controles funcionan dentro de los controles UpdatePanel. sin embargo, en algunas circunstancias, un control podría no funcionar como era de esperar si lleva a cabo alguna de estas acciones: • registrar scripts en la página usando métodos de la clase ClientScriptManager. esta clase se abandona en el caso de aplicaciones aJaX a favor de los méto-

50

tecnologías AsP.net 4.0 (saltando desde la versión 2.0)

dos equivalentes que ofrece el propio control ScriptManager, compatibles con las actualizaciones parciales. si necesitas registrar scripts como parte de un postback usa éstos últimos. Lo veremos con detalle en el próximo capítulo. • el uso directo de casi cualquier método de la clase HttpResponse no se llevará bien en general con las llamadas asíncronas. Por eso debemos evitar la llamada directa a los métodos: BinaryWrite, Clear, ClearContent, ClearHeaders, Close, End, Flush, TransmitFile, Write, WriteFile y WriteSubstitution. especialmente habitual es la generación directa de scripts o etiquetas hTML durante el repintado de un control o durante el procesado de un evento mediante llamadas al método Response.Write, que es algo que debemos evitar.

13.- AJAX coNTrol ToolkiT
Microsoft, conjuntamente con la comunidad de desarrolladores, ha creado el Kit de controles aJaX o AJAX Control Toolkit. se trata de un conjunto de más de 30 controles que permiten extender y dotar de funcionalidad extra avanzada a controles comunes de asP.NeT. La funcionalidad de estos controles se centra en el lado del cliente, es decir, en mejorar las características de los controles normales cuando ya están renderizados en el navegador, pero se añaden y configuran desde el servidor, por eso los he incluido en este capítulo. así, por ejemplo, gracias al Toolkit es muy sencillo hacer cosas como añadir un calendario desplegable a un TextBox para facilitar al usuario que escoja una fecha, o mostrar diálogos de confirmación integrados en la página, mejorar el aspecto de los mensajes de validación, crear animaciones, tener un editor hTML, etc. Por el mero hecho de utilizar estos controles obtendremos grandes mejoras en la usabilidad de las interfaces de nuestras aplicaciones Web asP.NeT. Los podemos usar en combinación con los repintados parciales o de manera completamente independiente, o lo que es lo mismo, requieren la existencia de un control ScriptManager en la página, pero no es necesario introducirlos dentro de un UpdatePanel ni utilizar éste ni ningún otro de los controles asP.NeT aJaX que hemos estudiado. su uso es extremadamente sencillo. Lo más complicado sea tal vez instalar el Toolkit, así que vamos a explicar cómo obtenerlo y ponerlo en marcha en nuestro sistema. el aJaX control Toolkit está disponible para descarga gratuita en codePlex (el directorio de proyectos open source de Microsoft), y en concreto en la siguiente dirección: http://ajaxcontroltoolkit.codeplex.com/Release/.

AsP.net AJAX en el servidor 51

Figura 15.- Página de descarga del AJAX control Toolkit

La última versión allí disponible funcionará sin problemas tanto en visual studio 2008 como en visual studio 2010. si estás usando visual studio 2005 con la versión 2.0 de la plataforma deberás buscar, un poco más abajo, las versiones anteriores específicas para este entorno. como se observa en la figura 15 la página ofrece tres variantes para descargar. • La primera versión “Binary” contiene únicamente las DLL necesarias para poder trabajar con el control desde visual studio. es la variante que necesitamos si queremos distribuir el Toolkit con alguna de nuestras aplicaciones o para instalar en un servidor en el que se vaya a utilizar éste con una aplicación allí desplegada. La variante “source” contiene, además de los binarios compilados, todo el código fuente de los diferentes controles. es una gran fuente de información y estudio si estamos interesados en cómo construir este tipo de controles. es la más apropiada para ser utilizada en nuestro equipo de desarrollo.

52

tecnologías AsP.net 4.0 (saltando desde la versión 2.0)

Por fin la variedad “scriptFilesonly” contiene únicamente el código Javascript que utilizan los controles para su funcionalidad en el navegador. se trata de la funcionalidad de los controles en su estado puro, para usar directamente en el navegador. esta es la variante que usarán los programadores más avanzados que emplearán directamente código Javascript para instanciar los diferentes Behaviours de estos controles en sus páginas.

en la mayor parte de los casos lo mejor es bajarse la primera o segunda variedad. Una vez descargado el ZIP correspondiente, descomprímelo en una carpeta de tu disco duro en la que vaya a estar disponible todo el tiempo, ya que una vez añadidos los controles a visual studio los usaremos siempre desde allí. La variante “Binary” descomprimirá ya directamente la DLL y carpetas asociadas que necesitamos. en el caso de la descarga con código fuente, la DLL que usaremos está dentro de la carpeta “Binaries” una vez descomprimido el ZIP. Dentro de la carpeta de binarios del Toolkit hay una DLL, AjaxControlToolkit.dll, que contiene toda la funcionalidad de los controles, así como una serie de carpetas con identificadores de idiomas (como “es”, “ar”, “cs”, “de”...) que contienen ensamblados satélites con los textos adaptados a cada uno de estos idiomas. ahora lanza visual studio y crea una nueva aplicación Web o abre cualquier aplicación Web que tengas ya hecha. edita una página asPX cualquiera para poder desplegar la barra de controles Web, como si fueras a añadir uno sobre la página. al desplegar la barra de herramientas pulsa con el botón derecho sobre alguna zona libre de ésta y en el menú contextual añade una nueva sección. Llámala “aJaX control Toolkit”. vuelve a pulsar con el botón derecho dentro del área vacía de esta nueva sección y elige la opción “elegir elementos” (“choose Items” en la versión en inglés del entorno):

Figura 16.- Añadiendo controles a la nueva sección que hemos creado.

AsP.net AJAX en el servidor 53

al hacer esto aparecerá, tras una cierta espera, un diálogo como el de la figura 17, en el que podremos elegir qué controles .NeT queremos que estén disponibles desde la barra de herramientas, y en concreto dentro de la sección actual.

Figura 17.- diálogo de gestión de controles

Lo único que resta por hacer es, con el botón Browse de la figura, localizar la DLL del aJaX control Toolkit (recuerda: AjaxControlToolkit.dll, en la carpeta correspondiente de la descarga anterior) y aceptar para que los controles se añadan automáticamente a la barra de herramientas.
Nota importante:
en visual studio 2010 este proceso fallará con un mensaje similar a este: “Could not load file or assembly ‘file:///C:\AjaxControlToolkit\AjaxControlToolkit.dll’ or one of its dependencies. Operation is not supported. (Exception from HRESULT: 0x80131515)” el motivo es el tratamiento especial que hace la versión 4.0 de .NeT de los ensamblados bajados de Internet. el sistema operativo marca con un indicador especial a los ensamblados descargados de cualquier recurso remoto (Internet o una carpeta compartida en la red local). De este modo pueden ser reconocidos como posibles amenazas de seguridad al proceder, a priori, de un origen no confiable como es Internet. El sistema operativo los trata de una forma dife-

54

tecnologías AsP.net 4.0 (saltando desde la versión 2.0)

rente debido a esto aunque los tengamos copiados ya en una carpeta local. sin embargo .NeT en sus versiones anteriores a la 4.0 hacía caso omiso de esta información sobre el origen y al estar ubicados en local los trataba como ensamblados confiables. Como mejora de seguridad en .NeT 4.0 se tiene en cuenta esta particularidad y limita la capacidad de todos estos .exe o .dll que se descarguen directamente o dentro de un ZIP desde una ubicación remota. Por este motivo la DLL del aJaX control Toolkit no es capaz de cargarse en el entorno de visual studio. Para solucionarlo lo que tenemos que hacer es desbloquear su funcionalidad. Para ello localiza la DLL en tu disco duro y en sus propiedades verás un botón “Desbloquear” que sirve precisamente para esto (ver figura 18). Necesitarás permisos de administrador para que el botón funcione, así que si estás en una carpeta de sistema (como por ejemplo la de Archivos de Programa) mueve antes la DLL a otro lugar, cambia este atributo y devuélvela a su sitio original.

Figura 18.- desbloqueo de ensamblado descargado desde sitio remoto. Ahora repite la operación anterior y verás como el problema desaparece y los controles se incorporan a tu barra de herramientas.

Una vez terminada la operación verás que en tu barra de herramienta aparecen multitud de controles nuevos, como se puede ver en la figura 19.

AsP.net AJAX en el servidor 55

Figura 19.- los controles del AJAX control Toolkit

aunque tengamos todos esos controles en la barra de herramientas, lo cierto es que, al trabajar con visual studio 2008 o visual studio 2010, no vamos a usarlos casi nunca desde ella. en visual studio 2005 sí que será necesario arrastrarlos desde ahí. en visual studio 2008 o superior las extensiones que ofrece el Toolkit están integradas con el entorno una vez las hayamos instalado. así, al seleccionar un control en la página como un TextBox u otro cualquiera, éste presentará entre sus tareas asociadas una nueva acción llamada “añadir extensor”:

Figura 20.- Acción de un control para extenderlo con el Toolkit

al pulsar sobre la acción se abre un diálogo en el cual podemos elegir qué acción extensora, proporcionada por los diferentes controles del Toolkit, queremos añadir a nuestro control. Para cada control ofrece las acciones más apropiadas. Por

56

tecnologías AsP.net 4.0 (saltando desde la versión 2.0)

ejemplo en la figura 21 estamos añadiendo un calendario desplegable a un control TextBox, de forma que cuando los usuarios lo seleccionen se mostrará para ayudar a introducir fechas.

Figura 21.- Extensión de un Textbox con un calendario desplegable.

esta extensor se corresponde con la funcionalidad del control CalendarExtender de la barra de herramientas (lo puedes identificar hacia la mitad de la columna de la derecha en la figura 19. a la hora de ejecutar la aplicación, todo el comportamiento que se observa en el navegador (en este caso la aparición del calendario y su funcionalidad) se proporciona directamente desde el lado cliente, mediante Javascript. No hay postbacks al servidor para ofrecer estas características. La funcionalidad es compatible con la mayor parte de los navegadores modernos (Internet explorer, Firefox, safari...).

Figura 22.- El extensor para calendarios en funcionamiento básico

AsP.net AJAX en el servidor 57

Por otro lado en la superficie de diseño en visual studio, a simple vista no ofrece ningún signo distintivo de que el control está usando esta extensión. si desplegamos las acciones para el control, ahora además de tener la posibilidad de añadir extensiones (podemos añadir cuantas necesitemos), también está la opción para quitar alguna de las que se están usando. otro signo visible de que el control está siendo extendido es que en sus propiedades ahora hay una nueva sección denominada “extensores” que ofrece, agrupadas, las propiedades que controlan el comportamiento de cada uno de estas extensiones. en la figura siguiente se ven las propiedades para el calendario desplegable, con las que podemos amoldar su forma de funcionar a nuestras necesidades.

Figura 23.- Propiedades de controles extensores

en realidad la mayor parte de los controles del Toolkit son muy fáciles de utilizar y en esta obra no vamos a entrar en detalles sobre el funcionamiento particular de cada uno de ellos. en la dirección: http://www.asp.net/ajax/ajaxcontroltoolkit/samples/ se puede encontrar una aplicación de referencia en la que, uno a uno, se presenta cada control, su sintaxis y sus posibilidades. si visitas la página podrás verlos en funcionamiento, y conocer los detalles de cómo sacarle partido.

58

tecnologías AsP.net 4.0 (saltando desde la versión 2.0)

Figura 24.- Página de demostración y documentación del control Toolkit

esta misma aplicación de ejemplo se incluye dentro de la carpeta “sampleWebsite” de la variante “source” de las descargas del Control Toolkit, así que puedes ejecutarla en local y mirar cómo está hecho cada ejemplo para aprender más todavía sobre ellos. en el código de ejemplo descargable desde la web del libro he incluido una pequeña página de ejemplo (“controlToolkit.aspx”) en la que un cuadro de texto se extiende con un calendario y una marca de agua para indicar el formato a los usuarios. el botón de envío se extiende con un control ConfirmButtonExtender de manera que nos pregunte si queremos enviar los datos al pulsarlo, y los dos validadores incluidos se han extendido con sendos controles ValidatorCalloutExtender para dotarlos de mayor vistosidad. ejecútalo para ver cómo, con sólo añadir estos extensores y sin código adicional, la interfaz se puede mejorar mucho.

Figura 25.- un ejemplo de validador mejorado

Merece la pena dedicarle un tiempo para conocer bien el aJaX control Toolkit, ya que mejorarán mucho la calidad de tus interfaces con un mínimo esfuerzo.

cAPíTulo

4

AsP.net AJAX en el navegador
con todo lo visto hasta ahora en el capítulo anterior, ya tenemos herramientas suficientes para desarrollar impactantes aplicaciones Web de alta velocidad de respuesta, sacando partido a los repintados parciales. Pero... ¿tendremos siempre realmente respuestas rápidas en la interfaz? ¿es realmente todo tan fácil como parece? el ser humano no se caracteriza precisamente por la mesura en sus acciones. La propensión natural en cualquier ámbito es a abusar de aquellas cosas que nos gustan o nos reportan beneficio sin pararnos mucho a pensar en las consecuencias, sobre todo si son fáciles y parecen no tener efectos negativos. Por ello cuando los programadores Web con asP.NeT, acostumbrados a los postback, descubren las posibilidades que les otorga el binomio ScriptManager-UpdatePanel, lo habitual es que empiecen a usarlo en todas partes. al fin y al cabo es muy fácil y “no hay que saber demasiado”: se arrastran un par de controles a una página normal y todo funciona de maravilla. además, toda aplicación Web moderna que se precie debe ofrecer en la medida de lo posible este tipo de funcionalidad. el resultado habitual es que se colocan varios controles UpdatePanel en todas las páginas o, peor todavía, se mete un gran UpdatePanel en la página que contiene a todos los demás controles (créeme, lo he visto). Tras desarrollar toda la funcionalidad se prueba la aplicación y todo funciona de maravilla: rápido, sin molestos refrescos de la página y con un aspecto superprofesional. Los problemas vienen cuando la aplicación se pone en producción en el servidor. en las primeras pruebas se nota todo algo más lento pero no parece realmente preocupante. al fin y al cabo estamos accediendo a través de Internet, no de la red local, y es normal que la velocidad sea algo menor. Pasan los días y las semanas. a medida que los usuarios de la aplicación aumentan, ya que tenemos más clientes o visitas, la cosa empieza a ir cada vez peor. Llega un punto en el que trabajar con la aplicación Web se vuelve insoportable de lo lenta que va. Miramos nuestro código para ver si tenemos alguna consulta a la base de datos que nos esté cargando el servidor y miles de posibilidades más, pero el problema de rendimiento no aparece. ¿Qué está pasando aquí? Lo que está pasando es que, a pesar de la aparente ligereza de las llamadas asíncronas con los Updatepanel, lo cierto es que (como ya se ha dicho) cada postback
59

60

tecnologías AsP.net 4.0 (saltando desde la versión 2.0)

asíncrono es un postback completo. esto implica que se envía toda la información de todos los controles de la página al servidor, incluyendo el ViewState. en la página se recrea el árbol de controles, se procesan todos y cada uno de los eventos de éstos así como los de la página. al terminar se devuelve el nuevo ViewState y el nuevo hTML para renderizar. La infraestructura de aJaX se queda sólo con el hTML correspondiente al UpdatePanel que se va a refrescar, pero en realidad se ha tenido que mover la página completa desde el cliente (el navegador) al servidor. así que, como dicen los anglosajones: “There’s no such thing as a free lunch”, es decir, que nadie da algo a cambio de nada. Y en este caso la máxima es cierta también. No quiero decir que las técnicas que hemos visto hasta ahora sean dañinas para el rendimiento. De hecho se pueden aplicar en sitios más o menos grandes sin problema. No obstante no se deben utilizar de cualquier manera, y en muchos casos habrá que medir bien el uso que se hace de ellas. su empleo indiscriminado sin entender realmente lo que está pasando por debajo, conduce a muchos problemas de rendimiento. si tenemos claros los conceptos y buscamos la forma de hacer las cosas lo mejor posible en cada circunstancia nos ahorraremos muchas frustraciones y problemas en el futuro. es por esto que Microsoft no concibió asP.NeT aJaX únicamente como un puñado de controles de servidor, sino que éstos se apoyan en una rica parte de programación en el lado cliente, de la que nosotros podremos aprovecharnos también para conseguir aplicaciones ágiles y optimizadas. este capítulo profundiza en las principales técnicas de trabajo en el lado del navegador conducentes a mejorar mediante Javascript las características de las páginas aJaX así como su rendimiento. Para seguir los próximos apartados es recomendable que tengas unos conocimientos fundamentales del lenguaje Javascript así como de hTML y manipulación del DoM del navegador. si no cumples estos requisitos puede que algún ejemplo concreto te cueste comprenderlo, pero si investigas un poco el código por tu cuenta no tendrás problema al final. vamos allá...

1.- rETrollAMAdAS dE rEd A MéTodoS ESTÁTicoS
Imagínate el siguiente ejemplo: tienes un formulario Web que contiene una rejilla llena de datos, varios controles de texto para entrada de información, algunos botones, listas desplegables para filtrado así como algunas etiquetas y controles de menor entidad. en una esquina hay una etiqueta que muestra un dato importante, un simple número (por ejemplo el stock en un almacén), y que el usuario de vez en cuando le gustaría ver actualizado. Dado que el resto de la página permanece inalterado, sólo por actualizar esa pequeña etiqueta no vamos a hacer un postback al servidor y recargar toda la enorme página ¿verdad?. aparentemente se impone el uso de un útil UpdatePanel para con-

AsP.net AJAX en el navegador 61

seguir un repintado parcial de la etiqueta. así que allá vamos nosotros: lo colocamos rodeando a la etiqueta, establecemos el botón de refresco como disparador “et voilà”. Listo para poner en producción. Lo malo es que no nos hemos dado cuenta de que, cada vez que se refresca la pequeña etiqueta, en realidad estamos procesando la página completa en el servidor y transmitiendo a través de la red enormes cantidades de información. sólo el ViewState de la página puede ocupar cientos de Kb. el resultado: un rendimiento nefasto cuando lo ponemos al alcance de los usuarios.

1.1.- Análisis de tráfico con un updatePanel
vamos a ver el efecto con un ejemplo muy sencillo. Desarrollaremos el mismo ejemplo con una página común que utiliza el control UpdatePanel, y luego veremos lo mismo usando código Javascript super-eficiente para comprender las diferencias. ello nos dará una idea muy buena de cuándo merece la pena aplicar una u otra técnica. el proyecto completo está disponible en el ZIP con los ejemplos de código que puedes descargar de la web de Krasis Press. Lo encontrarás en la carpeta “aJaX_ servicioscliente”. abre visual studio y crea una nueva aplicación Web. elimina la página por defecto “Default.aspx” y añade una nueva con el nombre “horaconUpdatePanel.aspx”. arrastra sobre ella un ScriptManager, un UpdatePanel y, dentro de éste último, una etiqueta (roja y con letras grandes) y un botón. el aspecto de la página debería ser similar al de la figura 1.

Figura 1.- Aspecto de nuestra página de ejemplo

como código para el evento de servidor que responde a la pulsación del botón vamos a escribir simplemente una línea que mostrará, dentro de la etiqueta, la hora actual en el servidor:
Protected Sub cmdDameHora_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles cmdDameHora.Click lblHora.Text = DateTime.Now.ToLongTimeString() End Sub

62

tecnologías AsP.net 4.0 (saltando desde la versión 2.0)

Ya está todo lo necesario. Ahora lanza la aplicación y pulsa el botón varias veces. Verás que la hora se muestra actualizada en la etiqueta a toda velocidad y sin problema alguno. Vamos a ir más allá de la superficie y veamos qué está pasando por debajo. Para ello vamos a utilizar una de mis herramientas favoritas: Fiddler. Esta utilidad gratuita ha sido creada por Eric Lawrence de Microsoft (aunque sin soporte oficial por la compañía). Actúa como proxy en todas las peticiones Web que se hacen en el equipo y permite inspeccionarlas de forma que podamos conocer sus datos, la información devuelta en ellas, modificarlas al vuelo, y un sin fin de utilidades más. Se puede descargar desde www.fiddler2.com. Si ponemos en marcha Fiddler y pulsamos el botón que actualiza la hora en la etiqueta veremos que se produce una llamada al servidor. Si la inspeccionamos para ver sus contenidos obtenemos los datos que muestra la figura 2:

Figura 2.- contenidos de la llamada al servidor con updatePanel

aquí vemos que la llamada, dado que debe enviar el ViewState de los controles, lleva 268 bytes de información. Y eso en esta página tan sumamente simple. Ima-

AsP.net AJAX en el navegador 63

gínate lo que enviará en páginas más complejas como la del ejemplo al principio de este epígrafe. Pero eso no es todo. La respuesta que contiene el nuevo Viewstate así como diversa información necesaria para actualizar el UpdatePanel ocupa 708 bytes. es decir que en total, para actualizar una información -la hora- que ocupa únicamente 8 caracteres (o sea, 8 bytes) estamos generando un tráfico de 976 bytes, es decir, ¡122 veces mayor de lo necesario!. además a esto hay que añadir la sobrecarga en el servidor, en el que se ha generado el árbol de controles y se han ejecutado todos eventos de la página y sus controles tan sólo para poder asignarle el valor a la etiqueta. esto usa procesador y memoria y reduce la escalabilidad de la aplicación. Parece que al final lo de usar siempre un UpdatePanel no es tan buen negocio después de todo.

1.2.- Ejemplo optimizado con retrollamadas de red a métodos estáticos
está claro que en este ejemplo, a pesar de lo espectaculares que son los números relativos, el impacto es mínimo. Y en muchos otros casos puede que también sea así, y el uso de un UpdatePanel está más que justificado gracias a la productividad que obtenemos. No obstante vamos a ver cómo podemos crear una versión de esta misma página que esté optimizada y nos genere el mínimo tráfico posible. así aprenderemos a solventar casos más graves en los que esta sobrecarga adicional no esté justificada o nos produzca problemas graves de rendimiento. Para ello vamos a explicar lo que se conoce como Retrollamadas de Red a Métodos Estáticos, o de manera más común, Métodos de Página. esta técnica, como su propio nombre indica, nos permite definir dentro de la página uno o más métodos que serán accesibles directamente a través de Internet desde el navegador, siendo sólo necesario el uso de código Javascript. De este modo en lugar de llamar a la página completa, que es lo que hacíamos antes, ahora llamaremos exclusivamente al método en cuestión, obteniendo una respuesta optimizada como enseguida veremos con el ejemplo. añade otra página al proyecto con el nombre “MetodosDePagina.aspx”. arrastra a su superficie de diseño un control ScriptManager. vete a la vista de código fuente de la página e introduce una etiqueta <div> para definir una capa hTML. otórgale el nombre “divhora” y en su estilo asígnale un color rojo y un tamaño de texto grande. Nos servirá para mostrar la hora y hará las veces de la etiqueta en nuestra versión optimizada del ejemplo anterior. arrastra también, desde el grupo hTML, un botón hTML normal. en su atributo onclick asígnale el código Javascript “Mostrarhora();”, que enseguida definiremos.

64

tecnologías AsP.net 4.0 (saltando desde la versión 2.0)

el código final debería tener este aspecto:

Figura 3.- código hTMl de nuestro ejemplo optimizado

a excepción del ScriptManager no estamos utilizando controles de servidor, pero en cualquier caso daría igual porque, como veremos, ya no será necesario enviar el ViewState ni otros elementos de formulario que hubiese en la página asPX. en el archivo Code Beside de la página (el .vb o .cs correspondiente) vamos a definir una sencilla función llamada DameHora() que devolverá una cadena con la hora actual en el servidor:
<WebMethod()> _ Public Shared Function DameHora() As String Return DateTime.Now.ToLongTimeString() End Function

al igual que si de un miembro de un servicio Web asMX se tratara decoramos este método con un atributo WebMethod. esto es indispensable para llamarlo desde el lado cliente. otra cuestión importante a tener en cuenta es que el método que definamos para ser llamado desde el cliente debe ser un método estático (o Shared en visual Basic). el motivo es que así no será necesario instanciar la página, lo que provocaría la misma carga que en el caso anterior al reproducir el árbol de controles, etc... el último detalle que nos queda es asignar a True la propiedad EnablePageMethods del ScriptManager, que por defecto contiene un False.
Nota:
esta propiedad hay que establecerla sobre el verdadero ScriptManager que vayamos a utilizar. No se puede establecer en un ScriptManagerProxy por lo que si el nuestro está en un Master Page debemos marcarlo en ésta directamente y quedará habilitado para todas las páginas que la usen.

AsP.net AJAX en el navegador 65

cuando se cree el código hTML final resultante de ejecutar la página, la propiedad EnablePageMethods hará que se genere automáticamente en el navegador el código Javascript necesario para llamar al método de la página. De hecho la llamada en Javascript será tan directa y fácil como escribir:
PageMethods.NombreMetodo

siendo NombreMetodo el nombre de alguno de los métodos de página que hayamos definido. a este método de Javascript se le pasan dos argumentos: la función que se usará para recibir los resultados de la llamada, y la función a la que se llamará si se ha producido algún error. con estas premisas, todo el código Javascript que necesitaremos escribir en el navegador será el siguiente:
<script language=”javascript” type=”text/javascript”> <!-function MostrarHora() { PageMethods.DameHora(finLlamada, gestorErrores); } function finLlamada(resultado) { $get(“divHora”).innerHTML = resultado; } function gestorErrores(excepcion) { $get(“divHora”).innerHTML = “Error en la llamada: “ + excepcion.get_message(); } // --> </script>

recuerda que la función MostrarHora es la que definimos para ser llamada cuando se pulse en el botón. La función $get() es una extensión global al lenguaje Javascript de las muchas que define asP.NeT aJaX y que nos permite acelerar la escritura de código. en este caso $get es equivalente a llamar al método estándar del DoM, document. getElementById, es decir, devuelve una referencia al elemento hTML cuyo nombre le pasemos como parámetro. en el ejemplo nos devuelve una referencia a la capa divHora de la página. con su propiedad innerHTML podemos introducir el hTML que necesitemos en su interior, en este caso la hora o el mensaje de error según lo que ocurra. ejecuta la nueva página y pulsa el botón de actualizar la hora del servidor. verás como de inmediato se muestra dentro de la capa en color rojo y funciona tan bien como la versión anterior. en apariencia no hemos ganado gran cosa puesto que todo parece ir igual de bien que en el ejemplo precedente. como antes, vamos a analizar las peticiones al servidor

66

tecnologías AsP.net 4.0 (saltando desde la versión 2.0)

para ver qué información estamos intercambiando. si capturamos con Fiddler la petición generada por el método de página veremos el resultado de la figura 4.

Figura 4.- contenidos de la llamada al servidor con un método de página

¡ahora sí que hemos optimizado el trabajo! La petición inicial ocupa 0 bytes, o sea va vacía porque no hay dato alguno que enviar. La respuesta nos devuelve en este caso el resultado de ejecutar el método en el servidor, codificado en formato JsoN, listo para ser usado desde Javascript. su tamaño: 16 bytes. Y eso porque hay 8 bytes extra que se añaden a todas las respuestas debido a la definición del objeto JsoN devuelto. si el resultado fuera un dato más largo seguirían siendo sólo 8 bytes más y su importancia relativa sería menor aún. es decir, hemos afinado al máximo el trasiego de información entre cliente y servidor, así como el código que ha de ejecutarse en el servidor, ya que no hay sobrecarga alguna por la ejecución de la página. Las ganancias de tamaño y rendimiento son mucho más espectaculares aún en páginas más grandes (reales) con más controles. si vemos el código fuente de la página generada podremos analizar con detalle el código Javascript autogenerado por el ScriptManager para conseguir toda esta funcionalidad. en la figura 5 se puede ver un fragmento del mismo.

AsP.net AJAX en el navegador 67

Figura 5.- código autogenerado para habilitar los métodos de página

es evidente que para ciertos casos esta técnica ofrece grandes ventajas.

2.- llAMAdAS A SErvicioS WEb
La técnica anterior está bien para trabajar dentro del ámbito de una misma página, pero presenta algunas limitaciones. La más importante de ellas es que nos dificulta la reutilización del código, ya que los métodos están atados a una página concreta. si necesitamos utilizarlo en más de una página del sitio nos veremos obligados a definir otros tantos métodos estáticos de página. otra limitación es que si usamos Master Pages en nuestro desarrollo y éstas son las que contienen el ScriptManager tendremos que habilitar la propiedad EnablePageMethods en éste, sea necesario o no para cada una de las páginas. ello provocará que se genere código Javascript para llamar a métodos de página que es innecesario en muchas de nuestras asPX, ya que no los utilizarán. Una evolución mejorada de lo anterior es la capacidad del ScriptManager para habilitar las llamadas a servicios Web desde Javascript. ello nos permite tener en una única ubicación común todo el código reutilizable que vamos a llamar desde el navegador, eliminando la primera de las limitaciones anteriores. además se puede habilitar selectivamente el acceso a estos servicios desde el control ScriptManagerProxy, por lo que sólo generaremos código Javascript extra en las páginas que realmente lo necesiten.

68

tecnologías AsP.net 4.0 (saltando desde la versión 2.0)

Nota:
Por supuesto sólo podremos hacer llamadas a servicios Web que se encuentren ubicados en el mismo dominio que nuestras páginas. esto no es una limitación de asP.NeT aJaX sino una característica de seguridad de todos los navegadores del mercado. si no existieran esta y otras limitaciones similares (como la que restringe el acceso sólo a las cookies de nuestro mismo dominio), nuestra privacidad y seguridad como usuarios de Internet se verían seriamente amenazadas.

2.1.- definición de un servicio Web ASMX para ser llamado desde JavaScript
vamos a aprender a utilizar los servicios web desde Javascript mediante el desarrollo de un ejemplo práctico.
Nota:
No voy a explicar aquí los fundamentos de los servicios Web y su creación, ya que es algo que se sale del ámbito de este capítulo. se supone que si estás leyendo este libro deberías saberlo ya. si no es así te recomiendo mi libro de fundamentos de asP.NeT donde viene explicado. También podemos usar del mismo modo servicios creados con WcF (Windows Communication Foundation), algo que haremos en otros ejemplos.

crearemos un pequeño proyecto que nos permita calcular la longitud de cualquier cadena con un método en un servicio Web, llamando a éste desde Javascript. obviamente hacer esto en el servidor es un sinsentido y es sencillísimo hacerlo directamente con Javascript, pero lo utilizaremos únicamente para ilustrar los conceptos necesarios para consumir servicios Web desde el lado cliente. agrega al proyecto un nuevo servicio Web llamado “contador.asmx”. elimina el método de ejemplo que trae y crea uno nuevo llamado ContarPalabras, cuyo objetivo será el de devolver el número de palabras contenidas en la cadena que se le pase como parámetro. el código es extremadamente sencillo pues sólo nos interesa centrarnos en cómo llamarlo desde Javascript, pero podría ser todo lo complicado que quisiésemos, con llamadas a bases de datos o lo que fuera necesario. en la figura 6 se puede ver el resultado.

AsP.net AJAX en el navegador 69

Figura 6.- El código de nuestro servicio Web de ejemplo

Tal y como está ahora mismo se trata de un servicio Web normal y corriente que acepta peticiones hTTP y devuelve sus resultados con XML. Por lo tanto sería factible realizar llamadas al mismo desde Javascript usando el objeto XMLhttprequest y procesando en el navegador el XML devuelto para operar con él. De hecho, como hemos visto en el capítulo de fundamentos, esta es la esencia original de aJaX (Asynchronous JavaScript And XML). sin embargo también hemos visto que existen formas mejores de utilizar este tipo de recursos remotos. en concreto utilizando JsoN (JavaScript Object Notation) para devolver los resultados. Y además sería fantástico si de alguna manera no tuviésemos que escribir a mano todo el código necesario para realizar la llamada y procesar los resultados ya que, como hemos estudiado, es algo engorroso y propenso a ciertos errores y situaciones problemáticas. si te fijas en la parte de arriba del código en la figura anterior verás que está comentado un atributo de la clase que representa al servicio Web. este atributo se llama ScriptService. su propósito es habilitar la generación automática de código Javascript especializado en el manejo del servicio con el que se decore. este código autogenerado permitirá realizar las llamadas a los métodos del servicio directamente desde el navegador sin tener que preocuparnos de los detalles de bajo nivel. esta “magia” se consigue gracias a un nuevo manejador para archivos .asmx que viene definido en asP.NeT aJaX y que sustituye al manejador habitual. si abrimos

70

tecnologías AsP.net 4.0 (saltando desde la versión 2.0)

el web.config de nuestra aplicación y buscamos en el XML veremos que tenemos unos nodos como los siguientes:
<httpHandlers> <remove verb=”*” path=”*.asmx”/> <add verb=”*” path=”*.asmx” validate=”false” type=”System.Web. Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35”/> </httpHandlers>

seguramente tendrá algunas entradas <add> más, pero nos quedamos con esta que es la que nos interesa. Lo que hace esta sección de la configuración es eliminar el manejador de servicios Web por defecto (nodo <remove>) y añadir uno nuevo especializado, ScriptHandlerFactory, que sabe interpretar este nuevo atributo que estamos estudiando y generar los formatos adecuados. No es necesario que toques nunca estos elementos. simplemente quería llamar tu atención sobre ellos para que sepas cómo funciona esto por debajo. Para seguir con el ejemplo, descomenta el atributo ScriptService en el código del servicio para que pueda funcionar en modo Javascript. Lanza la aplicación y navega hasta el servicio. si escribimos el sufijo /js a continuación del nombre del .asmx, se generará automáticamente el código Javascript necesario para llamarlo y podremos descargarlo a disco para su examen. La figura 7 muestra cómo hacerlo:

Figura 7.- generación automática de un proxy JavaScript para el servicio.

AsP.net AJAX en el navegador 71

este será el Javascript que usaremos de manera transparente en nuestra página para llamar al servicio cuando acabemos con el ejemplo. es útil saber que podemos obtenerlo así, ya que nos permitirá examinarlo y aprender sobre su modo de trabajar. De hecho existe la opción de obtener una versión de depuración del mismo, mucho más apropiada para su análisis ya que contiene comentarios y el código está ordenado, si en lugar de /js utilizamos el sufijo /jsdebug. en la figura siguiente se puede ver un fragmento de los contenidos de este código que como vemos incluso lleva comentarios XML para generar documentación y obtener ayuda Intellisense desde el entorno de visual studio.

Figura 8.- código JavaScript de la clase proxy para acceso al servicio

este código hace uso de clases especializadas de la parte cliente de asP.NeT aJaX, como por ejemplo Sys.Net.WebServiceProxy, que encapsulan toda la funcionalidad necesaria. su análisis va mucho más allá del ámbito de este libro pero te resultará fácil seguirlo si tienes buenos conocimientos de Javascript.

2.2.- creación de la página cliente para llamar al servicio
recapitulemos lo que hemos hecho hasta ahora, que es bien sencillo: hemos creado un servicio Web y lo hemos decorado con el atributo ScriptService para poder llamarlo desde Javascript directamente. ahora sólo nos resta crear una página de ejemplo que haga llamadas a nuestro flamante servicio y que ilustre la parte cliente del proyecto. agrega una nueva página al proyecto, “Usarcontador.aspx”. arrastra a su superficie desde la barra de herramientas un ScriptManager y, desde el grupo de controles hTML, un control <input> de texto, un <input> de tipo botón y un <div> para contener el resultado de contar las palabras con el servicio web. como vemos,

72

tecnologías AsP.net 4.0 (saltando desde la versión 2.0)

al igual que antes, no hemos utilizado ningún control de servidor a excepción del ScriptManager y todo el código que crearemos será de lado cliente.

Figura 9.- Nuestro ejemplo de uso del servicio Web

Lo único que tenemos que hacer para poder utilizar el servicio desde el Javascript de nuestra página es registrarlo en el ScriptManager. Para ello utilizaremos una colección especial de este control llamada Services, y que hasta ahora no habíamos visto. su misión es indicar al ScriptManager nuestra intención de utilizar uno o más servicios Web desde el navegador, consiguiendo que se genere automáticamente el código necesario para ello al renderizar la página. si seleccionamos el control y vamos a sus propiedades veremos que hay un editor para esta colección, en el que podemos añadir referencias a servicios visualmente.

Figura 10.- El editor visual de referencias de Scripts

Los elementos introducidos en este editor se reflejan como entradas dentro de un nodo <Scripts> en el código hTML de la página. De hecho, los programadores

AsP.net AJAX en el navegador 73

experimentados, dado que todo el código que van a escribir es de lado cliente, normalmente editan directamente este nodo en la vista hTML de la página, sin usar el diálogo visual anterior. visual studio nos ofrece soporte Intellisense para hacerlo (figura 11), por lo que es incluso más rápido.

Figura 11.- Establecimiento manual de referencias a servicios

esta colección está disponible también en el control ScriptManagerProxy, por lo que podemos usarlas en entornos con Master Pages. a partir del momento en que hemos registrado el servicio, visual studio nos ofrece soporte Intellisense para su uso desde Javascript (figura 12), obteniendo ayuda a medida que escribimos sobre sus métodos y propiedades (1) y sobre los diferentes parámetros de los mismos (2). esto nos facilita sobremanera el trabajo con este sistema.

Figura 12.- Soporte intellisense para el uso de los servicios con JavaScript

74

tecnologías AsP.net 4.0 (saltando desde la versión 2.0)

el código necesario para usar el servicio es directo y sencillo. accedemos al mismo a partir del nombre de la clase y el nombre de sus métodos, exactamente igual a cómo haríamos desde código de servidor. como parámetros se le pasaran los mismos que el método original en el servicio Web y, a mayores, una referencia a la función para procesar los resultados y otra a la función para gestionar los posibles errores. exactamente igual que hemos visto en para los métodos de pagina. el último parámetro posible (userContext, como se ve en la figura 12) es opcional y sirve para pasar información específica de la llamada de forma que podamos distinguir unas de otras si hacemos varias llamadas seguidas al servicio, o si usamos los mismo manejadores de éxito y fracaso para varios servicios diferentes. Nuestro código final es tan sencillo como este:
<script type=”text/javascript” language=”javascript”> <!-function LlamarServicioContador() { Contador.ContarPalabras($get(“txtFrase”).value, finLlamada, gestorErrores); } function finLlamada(resultado) { $get(“divRes”).innerHTML = resultado; } function gestorErrores(excepcion) { $get(“divRes”).innerHTML = “Error en la llamada: “ + excepcion.get_message(); } //--> </script>

como vemos prácticamente idéntico al que vimos cuando trabajábamos con métodos de página. ahora ya podemos ejecutar la aplicación. Introduciremos una frase en el campo de texto y al pulsar el botón veremos como de inmediato y sin refrescar la página obtenemos el resultado correcto en el div habilitado a tal efecto.

Figura 13.- Nuestro ejemplo en funcionamiento

el usuario no notará en absoluto que se ha ido al servidor a realizar la tarea. el trasiego de información entre cliente y servidor es mínimo, ya que sólo se envía y se recibe información codificada como JsoN (Figura 14). además tenemos toda la funcionalidad concentrada en un solo punto y accesible para toda la aplica-

AsP.net AJAX en el navegador 75

ción. al tratarse de un servicio Web podremos sacarle partido a características de éstos como, por ejemplo, la caché de datos para mejorar el rendimiento.

Figura 14.- Análisis de la petición realizada al servicio web

el uso de servicios Web y servicios WcF diseñados para comunicación desde Javascript en el cliente es realmente útil. Los podemos utilizar para intercambiar entre cliente y servidor todo tipo de datos complejos y no sólo simples cadenas y números, como hemos visto en los ejemplos. Desde el cliente además se pueden enviar datos al servidor (por ejemplo un registro de la base de datos para actualizar) y no sólo limitarnos a recibir información. De hecho las principales novedades de asP.NeT 4.0 en lo que respecta a aJaX tienen que ver con llevar cada vez más parte del procesamiento al lado del cliente y aprovechar la capacidad de llamar a servicios Web para generar las interfaces de datos puramente en Javascript. en el siguiente capítulo profundizaremos en todo ello.

3.- SErvicioS dE APlicAcióN: MEMbErShiP y rolES dESdE El NAvEgAdor
Uno de los servicios más útiles de asP.NeT es el de seguridad, encarnado en las clases Membership y Roles de System.Web.Security. Gracias a estas clases y el modelo de proveedores subyacente podemos gestionar de manera sencilla y flexible todas las operaciones de autenticación de nuestras aplicaciones Web.

76

tecnologías AsP.net 4.0 (saltando desde la versión 2.0)

con lo que hemos visto hasta ahora sería muy sencillo construir un servicio Web propio que nos permitiera hacer uso de todas las características de estas dos aPI directamente desde el navegador. De esta manera podríamos hacer autenticación de usuarios con Javascript, verificar si el usuario actual pertenece o no a un rol y en función de eso tomar decisiones ya desde el cliente, etc... Lo cierto es que, sí, sería muy fácil, pero ni siquiera tendremos que hacerlo. asP. NeT aJaX ofrece “de serie” un completo juego de clases Javascript especializadas que nos permiten utilizar las aPI de seguridad directamente desde el navegador. en realidad de lo que nos provee es de dos elementos que funcionan en cooperación. Por un lado, la primera pieza la tenemos en forma de dos nuevos recursos especiales en el servidor: • • /Authentication_JSON_AppService.axd /Role_JSON_AppService.axd

cada uno de ellos es un “endpoint” especial en el servidor al que nos podemos conectar para realizar llamadas con JsoN a diferentes funciones que, por debajo, hacen uso de Membership y Roles. ambos funcionan como un servicio Web normal y corriente basado en JsoN, como el que nos podríamos haber construido nosotros mismos con lo visto hasta ahora. La otra pieza está, por supuesto, en el lado cliente. se trata de las dos clases Javascript complementarias de los dos servicios anteriores que hacen uso de éstos para proveer de funcionalidades de seguridad al navegador. estas clases son: • • Sys.Services.AuthenticationService Sys.Services.RoleService

ambas clases disponen ya de los métodos apropiados para manejar los servicios Web correspondientes, por lo que no tenemos que conocer el funcionamiento de éstos para poder utilizarlos. De hecho los dos recursos .axd del servidor que he mencionado no están documentados, lo que es una forma de forzarnos a utilizarlos únicamente desde el Javascript de asP.NeT aJaX.
Nota:
si bien no están documentados ni tienen un WsDL para el servicio, resulta muy sencillo averiguar su funcionamiento usando alguna herramienta de intercepción de llamadas como Fiddler. con ella podemos ver rápidamente qué métodos se llaman y con qué parámetros por lo que resultaría fácil sacar partido a estos servicios de seguridad desde aplicaciones escritas en otros lenguajes o usando framewoks de desarrollo Javascript que no sean asP.NeT aJaX, como jQuery u otros. Una posible idea es integrar la autenticación de una aplicación antigua escrita en asP 3.0 clásico con la seguridad de asP.NeT. hay quien lo está utilizando para implementar la seguridad de sus aplicaciones silverlight o PhP.

AsP.net AJAX en el navegador 77

estos servicios vienen desactivados por defecto por lo que, antes de pasar a la parte práctica de cómo usarlos, debemos explicar cómo los activamos para que estén disponibles desde el lado cliente. La forma de poner en marcha los servicios de Membership y Roles para lado cliente es desde el archivo de configuración de la aplicación Web, web.config, añadiendo los siguientes nodos:
<system.web.extensions> <scripting> <webServices> <authenticationService enabled=”true”/> <roleService enabled=”true”/> </webServices> </scripting> </system.web.extensions>

Por lo demás la configuración es la misma que haríamos para un uso normal de estas aPI, es decir, establecer qué proveedores queremos usar para la autenticación y los roles y crear los usuarios y roles iniciales pertinentes. Generalmente en una aplicación pública accesible desde Internet usaremos el proveedor por defecto, que maneja los datos de usuarios y los roles dentro de una base de datos sQL server. en realidad funcionaría con cualquier proveedor que queramos, como por ejemplo el que valida los datos contra el Directorio activo y que se entrega también con asP.NeT. Por supuesto podemos utilizar las aPI desde el cliente (con Javascript y aJaX) y desde el servidor (llamadas normales a las clases) al mismo tiempo y de manera indistinta. si autenticamos desde aJaX estaremos autenticados para código de servidor, y viceversa. Para ver el funcionamiento en la práctica de estas clases crearemos un ejemplo. el código completo puedes verlo en el archivo “autenticacion.aspx” dentro de la carpeta “aJaX_servicioscliente” en el ZIP con las descargas del libro. en el ejemplo incluido existe una base de datos con tres usuarios (admin, Tipo1 y Total) y tres roles (administradores, Usuarios Tipo 1 y Usuarios Tipo 2), y en la página de ejemplo usaremos Javascript para autenticar y cerrar la sesión de estos los usuarios, mostrando información sobre los mismos. además hay una página protegida declarativamente con su correspondiente web.config dentro de la carpeta “Privado”, a la que sólo podremos acceder si antes nos hemos autenticado. Lo que hace el ejemplo es autenticar a los usuarios con aJaX y mostrar en la misma página información sobre a qué roles pertenecen. cambia también la función del botón, de autenticar a cerrar sesión, según si el usuario está o no autenticado. en la figura 15 se puede ver el aspecto (más bien espartano) de esta página de ejemplo.

78

tecnologías AsP.net 4.0 (saltando desde la versión 2.0)

Figura 15.- usuario autenticado con AJAX mostrando información sobre sus roles.

Nota:
sería recomendable que descargases el código de ejemplo y lo tuvieses delante cuando leas lo que viene a continuación para ver in situ el código descrito y comprenderlo mejor. De otro modo te resultará mucho más difícil seguirlo.

en la página tenemos un par de controles de texto hTML para introducir el usuario y la clave de acceso, así como un botón que hará la llamada al servicio, para intentar autenticar al usuario o para cerrar la sesión si ya está autenticado. cuando se pulsa este botón tiene lugar las siguientes instrucciones:
function LogInLogOut() { if (Sys.Services.AuthenticationService.get_isLoggedIn()) Sys.Services.AuthenticationService.logout(null, HazLogout, HuboError, null); Else { Sys.Services.AuthenticationService. login($get(“txtLogin”).value, $get(“txtClave”).value, false, null, null, HazLogin, HuboError, null); } }

Lo primero que se hace es utilizar la propiedad isLoggedIn de la clase AuthenticationService para comprobar si hay algún usuario actualmente autenticado en el sistema. Devuelve un valor booleano para informar de ello. en caso de que lo haya, procedemos a cerrar su sesión con una llamada al método logout de la clase. si no lo está, llamamos por el contrario al método login para efectuar la autenticación

AsP.net AJAX en el navegador 79

validando las credenciales. en este caso, por simplificar, no hemos comprobado si los campos están vacíos o no antes de hacer la llamada. Para efectuar la autenticación y registro en el sistema del usuario el método login necesita varios parámetros aparte del nombre de usuario y la contraseña. en concreto la lista de parámetros que debemos pasarle es la siguiente: • • • userName: el nombre de usuario a validar. password: la clave correspondiente. isPersistent: booleano para indicar si la cookie de seguridad generada será persistente o no. es equivalente a marcar la casilla de “recordar al usuario” en el control Login. hará que la validación perdure aún después de haber cerrado el navegador y mientras no se llame al método de cerrar la sesión o pase el tiempo de caducidad estipulado. customInfo: este parámetro es de uso reservado y debe pasarse siempre un null. redirectUrl: Dirección UrL a la que redirige el navegador cuando la autenticación se realiza correctamente. si le pasamos un null, no hay redirección y la devolución de la llamada se gestiona en la página actual, que es el caso que veremos en el ejemplo. loginCompletedCallback: función a la que se llama automáticamente cuando el inicio de sesión termina. en ella determinaremos si la autenticación ha tenido éxito o no y realizaremos las acciones pertinentes en cada caso. failedCallback: referencia a la función Javascript que se llamará cuando la autenticación falle o se produzca un error. userContext: datos de contexto de la llamada. Generalmente se le pasará un null.

• •

• •

Lo más interesante es ver cómo son las funciones que gestionan la devolución de la llamada. Dado que las llamadas aJaX a los servicios son asíncronas, el código Javascript se sigue ejecutando y se notifica de manera automática su finalización llamando a la función de éxito o de fracaso según proceda. La función para gestionar los errores recibe como parámetros una referencia a la excepción, el contexto de la llamada (parámetro userContext, que normalmente será nulo) y el método que se ha llamado para llegar aquí (para poder reutilizar la función con varios métodos y así distinguir de cuál viene):
function HuboError(excepcion, contexto, metodo) { MostrarInfo(“se ha producido un error:<br/>” + excepcion. get_message()); }

MostrarInfo es una función auxiliar que he definido y que muestra por pantalla la cadena hTML que le indiquemos.

80

tecnologías AsP.net 4.0 (saltando desde la versión 2.0)

La función de resultado de autenticación tiene un delegado similar, pero en lugar de la excepción recibe como primer parámetro un valor booleano para indicarnos si la autenticación ha tenido éxito o no. así, en nuestro caso el código es de la siguiente manera:
function HazLogin(credencialesValidas, contexto, metodo) { if (credencialesValidas) { nomUsuario = $get(“txtLogin”).value;

Sys.Services.RoleService. load(MostrarInfoUsuarioActual, HuboError, null); //Vacío los campos $get(“txtLogin”).value = “”; $get(“txtClave”).value = “”; $get(“cmdLogin”).value = “Cerrar sesión”; } else { MostrarInfo(“Credenciales no válidas”); } }

si en el primer parámetro se pasa un true, es que las credenciales eran válidas y el usuario está dentro del sistema. es lo que controlamos en el condicional. en ese caso anotamos el nombre de usuario utilizado en una variable para poder guardarlo y provocamos la carga de la información sobre los roles a los que pertenece el usuario recién autenticado (enseguida vamos sobre eso).
Nota:
desde mi punto de vista es una pena que el servicio no disponga de un par de métodos para obtener el nombre y el identificador único del usuario actual. Ello ayudaría mucho a relacionarlo con otras entidades de la aplicación directamente desde el cliente. No existe, por tanto, forma alguna de averiguar el nombre del usuario actual una vez este se ha autenticado. Si lo necesitamos deberemos habilitar por nuestra cuenta alguna forma de obtenerlo desde el servidor. No obstante como la autenticación se hace tanto en cliente como en servidor podemos usar todos los controles habituales —como el LoginName, LoginStatus, LoginView y similares— para mostrar esta información en otras páginas.

en el código anterior la única línea relevante es la que efectúa la carga de información sobre los roles de usuario. hace una llamada al método load de la clase RoleService, el cual toma como parámetros la función a llamar al terminar la carga asíncrona de esta información (MostrarInfoUsuarioActual en este ejemplo), la que se llamará cuando haya un error (reutilizamos la que ya usamos para el método login), y un nulo para los datos de contexto. este método load llama de manera asíncrona al servicio de manejo de roles en el servidor y al devolverse la llamada ejecuta la función que le indiquemos, en este caso MostrarInfoUsuarioActual. a esta función le pasa un array de cadenas de texto

AsP.net AJAX en el navegador 81

que contiene la lista de roles a los que pertenece el usuario actual. en este caso nos limitamos a mostrarlos por pantalla recorriéndolos en un bucle:
function MostrarInfoUsuarioActual(roles) { var s; s = “Usuario actual: “ + nomUsuario + “<br/>”; roles.sort();

}

for(var i = 0; i < roles.length; i++) { s += “Rol: “ + roles[i] + “<br/>”; } MostrarInfo(s);

como ya he dicho, si el usuario está autenticado lo está tanto para el código de cliente como para el de servidor. Por ello la mayor parte de las verificaciones de seguridad se harán en el servidor. La clase AuthenticationService no ofrece método alguno para verificar la autorización de usuarios (por ejemplo para comprobar si tiene acceso a un determinado archivo), teniendo que hacerlo de la manera habitual en el servidor. La única función disponible para comprobar autorizaciones es la propiedad isUserInRole de la clase RoleService, a la que podemos llamar de la siguiente manera:
Sys.Services.RoleService.get_isUserInRole(“Administrador”)

obteniendo verdadero o falso en función de la pertenencia del usuario actual al grupo que se le pase en el parámetro. en definitiva, los servicios automáticos de autenticación y roles de asP.NeT aJaX nos facilitan el acceso desde el propio navegador al trabajo más común de seguridad. son más eficientes, en cuanto al uso de ancho de banda y carga del servidor, que utilizar dentro de un UpdatePanel los propios controles de seguridad que nos ofrece asP.NeT. No obstante tienen sus limitaciones y generalmente los usaremos sólo para hacer la autenticación y cierre de sesión, usando el resto de características desde el lado servidor. esto no supone una gran limitación ya que el objeto de este tipo de funcionalidades de lado cliente es el de reducir el trasiego de datos entre cliente y servidor, y las funciones de seguridad y control de acceso, una vez hecha la autenticación, no suelen afectar en absoluto al tráfico intercambiado, así que suelen ser apropiadas.

4.- rEFErENciAS A ScriPTS EN PÁgiNAS y coNTrolES
La principal función del ScriptManager es, tal y como se puede deducir de su nombre, la de registrar en el cliente diferentes bibliotecas de funciones Javascript

82

tecnologías AsP.net 4.0 (saltando desde la versión 2.0)

que dotan de la funcionalidad necesaria para hacer uso de las características de asP. NeT aJaX. Tradicionalmente en asP.NeT ha existido para este propósito una clase especializada llamada ClientScriptManager que permite registrar scripts de manera única en una página, de forma que nos aseguremos de que no se registran dos veces y que se genera el código hTML apropiado. esta clase dispone de los siguientes métodos: • RegisterClientScriptBlock: permite enviar, desde código c# o vB de servidor, un bloque de Javascript al cliente, otorgándole un nombre único, de forma que cuando se intente enviar más de una vez no se permita su duplicación. Genera un bloque de tipo <script></script>. RegisterClientScriptInclude: registra en el cliente una referencia a un script contenido en un archivo externo, normalmente con extensión .js. Genera una etiqueta de tipo <script src=...”/>. RegisterClientScriptResource: registra en el cliente una referencia a un script guardado como recurso en un ensamblado, de forma que no haya que distribuir el código en un archivo independiente. Genera una etiqueta de tipo <script src=”Webresource.axd?...” />.

el problema de esta clase tradicional accesible directamente desde las páginas, es que no funciona cuando participa en un repintado parcial de página. si por ejemplo tenemos un control que, dependiendo de los datos que maneja, genera Javascript usando los métodos anteriores en cada petición, al colocarlo en una página tradicional recibiremos nuevo Javascript en cada postback. como cada repintado de la página es completo y por lo tanto el hTML se vuelve a procesar entero, estos nuevos scripts se interpretan en cada ocasión y todo funciona perfectamente. si decidimos introducir un UpdatePanel para hacer repintados parciales de la página, de repente, todo ese código Javascript dejará de funcionar. el motivo es que al repintar sólo una parte de la página, aunque llegase el nuevo Javascript al navegador, éste no se interpreta y no tiene efecto alguno. el resultado de esto: muchos controles de terceros que funcionan perfectamente en páginas tradicionales dejan de hacerlo al usar asP.NeT aJaX. Para solucionarlo la clase ScriptManager ofrece exactamente los mismos tres métodos que acabamos de ver pero con la salvedad de que, por el mero hecho de usarlos desde ésta, ya funcionarán sin problema dentro de un UpdatePanel. se trata de métodos estáticos de la clase, por lo que no tenemos que tener siquiera un ScriptManager en la página para poder utilizarlos. Por lo tanto, en lugar de utilizar este código para registrar uno de nuestros scripts:
Page.ClientScript.RegisterClientScriptInclude(“Utilidades”, “~/Scripts/Utils.js”)

deberíamos usar mejor este otro:

AsP.net AJAX en el navegador 83

ScriptManager.RegisterClientScriptInclude(Page, GetType(Page), “Utilidades”, “~/Scripts/Utils.js”)

en este caso se debe especificar una referencia a la página o control que registra el script así como a su tipo (los dos primeros parámetros). Para facilitar más aún el registro de scripts compatible con repintados parciales, el control ScriptManager ofrece una colección scripts en la que podemos incluir las referencias de manera directa, sin escribir código en el evento Load de la página. esta colección funciona de modo similar al de la colección Services que hemos visto en el apartado anterior, y tiene su propio diálogo visual (figura 16) así como un nodo (<ScriptReferences>) en el código hTML de la página. Por ejemplo:
<asp:ScriptManager ID=”ScriptManager1” runat=”server”> <Scripts> <asp:ScriptReference Path=”~/Scripts/Utils.js” /> <asp:ScriptReference Assembly=”Recursos” Name=”Recursos. Scripts.Utils.js” /> </Scripts> </asp:ScriptManager>

en este caso estamos registrando una referencia a un script albergado en un archivo .js externo (primer ScriptReference), y otra a uno embebido como recurso en un ensamblado de forma que no tengamos que distribuirlo como archivo suelto.

Figura 16.- diálogo de referencia de Script

estas colecciones están disponibles también en el control ScriptManagerProxy, por lo que podemos usarlas en entornos con Master Pages.

84

tecnologías AsP.net 4.0 (saltando desde la versión 2.0)

Nota:
hasta la versión 3.5 de la plataforma (incluida) la clase ScriptReference heredaba directamente de la clase Object. al aparecer el service Pack 1 de .NeT 3.5 hubo un cambio interno en el código que hace que la clase ahora herede de System.Web.UI.ScriptReferenceBase. el efecto negativo de esto es que si en tu equipo de desarrollo tienes visual studio 2008 sP1 y despliegas la aplicación, pre-compilándola antes, a un servidor que tiene .NeT 3.5 sin el service Pack aplicado, obtendrás errores al intentar cargar las páginas con referencias a scripts. en visual studio 2010 con.NeT 4.0 aunque esto es así también no tendrás problemas porque ya deberás tener .NeT 4.0 en el servidor para funcionar, aunque puedes compilar para la versión 3.5 de la plataforma, en cuyo caso lo anterior aplica también.

5.- oPTiMiZAcióN dE uSo dE biblioTEcAS EN ASP.NET 4.0
aunque una página esté vacía sólo por el hecho de colocar un, aparentemente inocuo, control ScriptManager en ésta, obtenemos en el lado cliente un buen puñado de código de script, como se puede observar en la figura 17.

Figura 17.- Scripts generados por defecto al incluir un ScriptManager

AsP.net AJAX en el navegador 85

La funcionalidad de cliente de asP.NeT aJaX está en realidad separada en diversos apartados, por ejemplo, extensiones globales de Javascript, utilidades de formularios, operaciones de comunicaciones, globalización, seriación de objetos (JsoN), etc... si nuestra aplicación no requiere alguna de estas partes ¿por qué tenemos que descargarlas por completo en todas las ocasiones?. Lo ideal sería poder descargar selectivamente aquella funcionalidad que nos interese en cada caso. hasta la llegada de asP.NeT 4.0 no había posibilidad de conseguir esto. Un ScriptManager en la página implicaba que teníamos toda la funcionalidad disponible en el cliente, la necesitásemos o no. a partir de .NeT 4.0 ya no es necesario descargar los scripts completos aunque por defecto lo sigue haciendo para asegurar la compatibilidad con aplicaciones anteriores. el comportamiento se establece a través de la nueva propiedad MicrosoftAjaxMode, que puede tomar los siguientes valores: • • • Enabled: se envían todos los scripts. es el comportamiento por defecto para ser compatible con lo anterior. Explicit: cada script que necesitemos se debe añadir explícitamente con una sección <ScriptReference>. Disabled: todos los scripts propios de aJaX están deshabilitados.

Normalmente sólo usaremos el segundo valor para indicar manualmente qué partes de aJaX queremos usar. así, por ejemplo, si sólo queremos usar las extensiones básicas para Javascript que asP.NeT aJaX nos ofrece, podemos escribir esto:
<asp:ScriptManager ID=”ScriptManager1” runat=”server” EnablePartialRendering=”False” MicrosoftAjaxMode=”Explicit”> <Scripts> <asp:ScriptReference Name=”MicrosoftAjaxCore.js” /> </Scripts> </asp:ScriptManager>

Incluyendo exclusivamente el script correspondiente al núcleo de funcionalidad. Fíjate en que también he marcado EnablePartialRendering como False para evitar los scripts que generan la funcionalidad de los repintados parciales. Los módulos de funcionalidad que nos ofrece asP.NeT aJaX para el lado cliente son los siguientes: • • • • MicrosoftAjaxCore.js: extensiones base para el lenguaje Javascript y el DoM. MicrosoftAjaxComponentModel.js: soporte de controles Javascript y behaviours. MicrosoftAjaxSerialization.js: seriación y de-seriación de datos. MicrosoftAjaxGlobalization.js: globalización y localización de scripts.

86

tecnologías AsP.net 4.0 (saltando desde la versión 2.0)

• • • • • • •

MicrosoftAjaxHistory.js: soporte para manipular el histórico de navegación (nuevo en asP.NeT 3.5 sP1). MicrosoftAjaxNetwork.js: operaciones de comunicación a través de la red. MicrosoftAjaxWebForms.js: la funcionalidad para repintado parcial necesaria para que funcionen los UpdatePanels. MicrosoftAjaxWebServices.js: soporte para manejo de servicios Web desde el navegador. MicrosoftAjaxApplicationServices.js: servicios de autenticación, roles y perfiles. MicrosoftAjaxTemplates.js: plantillas de datos de lado cliente (nuevo en asP.NeT 4.0). MicrosoftAjaxAdoNet.js: soporte para aDo.NeT services (nuevo en asP. NeT 4.0).

aun pudiendo elegir cuáles queremos incluir en la página no podemos hacerlo de cualquier manera, puesto que algunos dependen de otros para hacer su trabajo. Por tanto deberemos ser cuidadosos y tener en consideración las subordinaciones existentes entre ellos a la hora de decidir cuáles incluimos. en la figura 18 se muestran las distintas relaciones existentes entre los módulos.

Figura 18.- relaciones de dependencia entre módulos de AJAX.

AsP.net AJAX en el navegador 87

Nota:

Los módulos Templates y AdoNet tienen un tono ligeramente distinto para indicar que son soportados sólo en ASP.NET 4.0 (los demás son de ASP.NET 3.5). El módulo de Templates, que veremos en el próximo capítulo, depende del módulo WebServices únicamente cuando se quiere utilizar el control DataContext para enlazado a datos con JavaScript (aparecido en 4.0). Del mismo modo este módulo sólo dependerá de AdoNet si se usa el control AdoNetDataContext. El módulo WebForms se ha puesto en un tono diferente porque para incluirlo basta con establecer la propiedad EnablePartialRendering del ScriptManager como True, que es su valor por defecto.

Como ya he dicho, el orden de inclusión es importante, y debemos asegurarnos de que los módulos de los que dependen otros módulos se colocan en primer lugar. Cuanto más arriba o más a la izquierda en la figura anterior, antes deben ponerse en la lista de referencias. También existe la posibilidad de descargarse de www.codeplex.com/aspnet, en su apartado para AJAX, el código fuente de todos estos scripts e incluirlos directamente como referencias <script> de HTML normales.

5.1.- combinación de scripts
otra cuestión importante a la hora de optimizar la descarga de scripts es tener en cuenta el número de referencias a scripts que incluye la página, ya que ¿qué es más eficiente: descargar 100 Kb en un sólo archivo, o el mismo tamaño de información en 10 archivos diferentes? obviamente lo primero porque nos estamos ahorrando 9 viajes de ida y vuelta al servidor. como hemos visto en la figura 16, cuando utilizamos el ScriptManager así como otros controles basados en aJaX, como los del control Toolkit, éstos generan una serie de referencias de tipo <script> en la página. cada una de ellas supone una petición al servidor por cada referencia, por lo que al final la carga de la página se traduce en multitud de llamadas al servidor. Para tener en cuenta esta realidad y permitirnos optimizar la descarga de código Javascript al navegador el ScriptManager, a partir de asP.NeT 3.5 sP1, ofrece una característica de combinación de scripts muy útil. Lo único que tenemos que hacer es rodear nuestro nodo <Scripts> en el ScriptManager con un nuevo nodo padre llamado <CompositeScript>, así por ejemplo:
<asp:ScriptManager ID=”ScriptManager1” EnablePartialRendering=”false” MicrosoftAjaxMode=”Explicit” EnableHistory=”true” runat=”server”> <CompositeScript> <Scripts> <asp:ScriptReference Name=”MicrosoftAjaxCore.js” /> <asp:ScriptReference Name=”MicrosoftAjaxComponentModel.js” /> <asp:ScriptReference Name=”MicrosoftAjaxSerialization.js” /> <asp:ScriptReference Name=”MicrosoftAjaxHistory.js” />

88

tecnologías AsP.net 4.0 (saltando desde la versión 2.0)

</Scripts> </CompositeScript> </asp:ScriptManager>

al hacer esto todos los archivos incluidos en la colección de scripts se combinan de manera automática en uno solo, de modo que ello se traduce en una única llamada al servidor para descarga de scripts. el tamaño sigue siendo el mismo pero al reducirse mucho el número de viajes hemos optimizado bastante el tiempo de respuesta de la página. En una página normal que use AJAX hay muchos más Scripts de los que pueda parecer a simple vista. En la página de Facebook de campusMVP (http://go.krasis. com/Facebook) podrás encontrar un vídeo práctico en el que el autor de este libro explica paso a paso cómo averiguar exactamente qué Scripts están siendo usados en una página, y cómo podemos posteriormente generar un script único como combinación de todos ellos para optimizar su descarga. El acceso directo al vídeo tiene esta URL: http://www.facebook.com/video/video.php?v=101426592913 si usamos la capacidad de indicar manualmente qué módulos de script queremos utilizar es que nos preocupa el rendimiento, así que deberíamos usar esta característica de combinación de scripts también. Tenla en cuenta para tus páginas más importantes.

6.- EN rESuMEN
en este capítulo hemos estudiado las cuestiones más importantes para el trabajo con aJaX en el lado del cliente. al no ser este un libro de Javascript, no hemos entrado en los detalles relacionados con la programación pura y dura en este lenguaje, que asP.NeT aJaX mejora mucho. Por el mismo motivo tampoco hemos visto algunas otras cuestiones relacionadas, como el uso de recursos Javascript localizados o la creación de behaviours de cliente. el objetivo ha sido mostrar los fundamentos que nos van a ayudar a conseguir una visión más “pura” del desarrollo con aJaX, en el que el navegador cobra una gran importancia, responsabilizándose de la mayor parte de la generación de la interfaz. esta visión es en la que se basan las características aJaX introducidas en asP.NeT 4.0 y que vamos a estudiar en el próximo capítulo.

cAPíTulo

5

enlazado a datos en el navegador
el modelo tradicional de trabajo de las tecnologías Web (asP, PhP, asP.NeT...) ha sido siempre el de generación de hTML en el servidor. vamos a repasar el funcionamiento normal de una página asPX que muestra datos enlazados desde algún origen (como una base de datos): 1. el usuario solicita la página al servidor. 2. se obtienen los datos necesarios, se enlazan y se genera el hTML final de la página a partir de los diferentes controles Web que tengamos en la misma (renderizado del hTML). 3. el hTML se recibe en el navegador que se encarga de visualizarlo. cuando el usuario efectúa cualquier acción que cambie el estado de la página (una ordenación, selección, borrado...), se envía el formulario al servidor (postback) y una vez allí se genera de nuevo todo el hTML para devolver al navegador. esto es así independientemente de que la página utilice aJaX con controles UpdatePanel o no. el principal problema de este modelo (tampoco exento de ventajas) es la cantidad de datos que hay que mover constantemente entre el cliente y el servidor: el Viewstate de la página y el propio hTML generado. ello provoca lentitud en la respuesta, carga en el servidor y multitud de “viajes” entre el cliente y servidor que muchas veces serían innecesarios. Un modelo alternativo sería el de traspasar más responsabilidades al navegador. en esta visión del desarrollo Web el servidor se encargaría de devolver únicamente dos cosas: las páginas hTML estáticas (es decir, no generadas dinámicamente) que solicite el usuario, y los datos que deban manejar éstas. el resto de la acción se desarrollaría en el navegador. Éste generaría dinámicamente la interfaz de usuario, gestionaría los eventos en local (en contraste con los eventos de servidor) y pediría o enviaría datos al servidor según la necesidad. Para entenderlo mejor comparemos una situación común como es la de mostrar los datos de una base de datos en una tabla. en el modelo tradicional, como hemos visto, obtendríamos los datos en el servidor y los enlazaríamos a un control de servidor (por ej. un GridView), el cual generaría la tabla y enviaría el hTML final al navegador.
89

90

tecnologías AsP.net 4.0 (saltando desde la versión 2.0)

en el modelo alternativo propuesto, la página prácticamente vacía llegaría al navegador. Éste solicitaría los datos a visualizar al servidor (mediante un servicio Web o similar) y se encargaría de generar la tabla correspondiente a partir de ellos, creando dinámicamente con Javascript los elementos necesarios. si se edita un registro de la tabla, en el primer caso se enviaría de nuevo la página al servidor, que actualizaría los datos y generaría de nuevo la página. en el segundo modelo el navegador enviaría únicamente los datos a actualizar al servidor, sin necesidad de hacer nada más porque el resto de la página permanece inalterada. Incluso podríamos acumular varios cambios en los datos y luego enviarlos todos juntos para su actualización en bloque, ahorrando viajes al servidor. La figura 1 muestra la comparación entre ambas concepciones del desarrollo Web.

Figura 1.- Modelo tradicional vs modelo “puro” de aplicaciones Web AJAX

este modelo alternativo de aJaX sería más “puro”, en el sentido de que sólo se intercambian datos y no información de la interfaz. acerca todavía más el mundo de las aplicaciones web al de las de escritorio, ya que en ambos casos la parte importante de la interacción con el usuario transcurre en el equipo de éste. al usar esta técnica de trabajo es evidente que se obtienen muchas ventajas, sobre todo en lo que respecta al rendimiento del servidor, capacidad de respuesta de la interfaz y disminución radical de la cantidad de información que se trasiega por la red. Las tecnologías que habilitan esta visión del desarrollo aJaX “puro” son dos: los servicios Web y las plantillas de lado cliente. en el capítulo anterior ya hemos podido comprobar lo sencillo que resulta emplear desde Javascript los servicios Web habilitados a tal efecto con asP.NeT aJaX. Las plantillas de lado cliente son una

enlazado a datos en el navegador 91

innovación aparecida con asP.NeT 4.0 que nos facilitan la gestión de interfaces complejas enlazadas a datos, directamente en el navegador.
Nota:
este apartado es tal vez el más complicado de todo el libro, por lo que se recomienda seguirlo con calma y apoyándose en el código de ejemplo. al trabajar con Javascript en el navegador, conseguir que todo funcione correctamente a la primera suele costar un poco, pero los resultados son muy interesantes. No desesperes si al principio algunas cosas no te funcionan, es muy habitual en este caso.

1.- coNcEPTo dE PlANTillAS dE lAdo cliENTE
Por regla general cuando tenemos que mostrarle al usuario datos homogéneos, utilizamos algún tipo de patrón de visualización que se repite de manera idéntica para cada elemento, particularizando sólo los valores de éstos. así, por ejemplo, si tenemos que mostrar una lista de productos por pantalla podemos utilizar una tabla similar a esta:
<table cellpadding=”5” cellspacing=”0” border=”1”> <thead> <tr> <th>Nombre</th> <th>Precio</th> ....

</tr> </thead> <tbody> <tr>

</tr> <tr>

<td>Producto 1</td> <td> 10 €</td> .....

</tr> ...... </tbody> </table>

<td>Producto 2</td> <td> 20 €</td> .....

Tenemos la cabecera de la tabla con los títulos de las columnas y lo que hay en el cuerpo es una fila por cada uno de los productos que visualiza la información de cada uno de ellos. Del mismo modo podríamos haber elegido cualquier otra

92

tecnologías AsP.net 4.0 (saltando desde la versión 2.0)

distribución para los elementos: con listas desordenadas <ul><li> o con elementos <span>, etc... La cuestión aquí es que claramente hay un patrón que se repite a la hora de mostrar los datos. este patrón que podemos identificar fácilmente es lo que va a constituir nuestra plantilla. en controles de servidor como el Repeater o el ListView trabajamos de una forma similar, pero el hTML se genera en el servidor para enviarlo al cliente. Imaginemos ahora que, tras haber identificado la parte común a los datos, pudiésemos escribir lo anterior de una forma parecida a la siguiente:
<table cellpadding=”5” cellspacing=”0” border=”1”> <thead> <tr> <th>Nombre</th> <th>Precio</th> .... </tr> </thead> <tbody id=”plantilla” class=”sys-template”> <tr> <td>{{ ProductName }}</td> <td>{{ UnitPrice }}</td> ..... </tr> </tbody> </table>

Fíjate que lo que hemos hecho es marcar una zona de nuestro hTML (lo que está dentro de la etiqueta <tbody>) como una zona genérica, indicando en ella los lugares donde irán los datos. ahora será el propio navegador el que identifique esta zona y la utilice como un patrón a repetir para generar la tabla que necesitamos de manera automática a partir de los datos a visualizar, traídos desde el servidor. ¿No es realmente cómodo? esta capacidad de autogenerar la interfaz a partir de plantillas nos brinda un gran control sobre el hTML generado y sobre todo nos desata del servidor a la hora de generar las interfaces de usuario. La definición de las plantillas en asP.NeT aJaX 4.0 es tan sencilla como lo que acabamos de ver, pues basta con identificar el elemento hTML que las contendrá, y marcar dentro del código interior los lugares en los que irán los campos. hacer uso de ellas tiene algo más de dificultad, sobre todo porque disponen de capacidades avanzadas como el enlazado bidireccional, refresco automático, actualización de datos en el servidor, etc. Para aprender utilizarlas en la práctica vamos a crear un ejemplo y analizaremos el código poco a poco.

enlazado a datos en el navegador 93

2.- lAS bASES PArA TrAbAJAr
Para el ejemplo vamos a partir de la base de datos “Northwind” que hemos usado a lo largo del libro y vamos a crear una página que muestre información sobre los productos que vende esta empresa ficticia. Lo primero que haremos es crear un servicio Web que facilite información sobre los productos. Nuestro servicio, de momento, tendrá un único método llamado GetAllProducts, que devolverá una colección enumerable de objetos Product con la información de todos los productos en la base de datos. el servicio se llama “Northwindservice.svc” y lo tenemos dentro de la carpeta “servicios”.
Nota:
en el ejemplo descargable desde la Web el servicio se ha implementado con Windows Communication Foundation, y la capa de datos con Linq2sQL. Tú puedes hacerlo como mejor te parezca, por ejemplo con aDo.NeT “clásico” y un servicio Web asMX de asP.NeT, como se ha visto en el capítulo anterior. en cualquier caso el servicio debe ser compatible con aJaX y debe devolver y aceptar objetos serializados en formato JsoN. No nos pararemos en la implementación del servicio ya que lo que nos interesa es centrarnos en la funcionalidad aJaX. consulta el código fuente en la descarga para ver los detalles.

añadiremos una página al proyecto que contendrá únicamente un control ScriptManager. como sabemos éste se encargará de generar todo el Javascript necesario para sustentar la funcionalidad de asP.NeT aJaX. hay que tener en cuenta una cosa, y es que el control no envía al cliente el código necesario para que funcionen las plantillas a menos que se lo indiquemos, para lo cual habrá que usar la referencia de script correspondiente:
<asp:ScriptManager ID=”ScriptManager1” runat=”server”> <Scripts> <asp:ScriptReference Name=”MicrosoftAjaxTemplates.js” /> </Scripts> </asp:ScriptManager>

Nota:

Es importante señalar que como toda la funcionalidad que vamos a crear es exclusivamente en el navegador, en realidad no nos hace falta siquiera este control. Lo usamos por comodidad en Visual Studio 2010. Basta con tener los scripts de ASP.NET AJAX en archivos .js externos (descargables desde http://www.codeplex.com/aspnet, en el apartado AJAX) y añadir referencias a los mismos en la página. Por ello todo lo explicado podría usarse con cualquier otra tecnología como PHP o incluso con páginas HTML puras y duras, sin código de servidor. Las referencias necesarias son las siguientes:
<script type=”text/javascript” src=”Scripts/MicrosoftAjax.js”> </script> <script type=”text/javascript” src=”Scripts/ MicrosoftAjaxTemplates.js”></script>

94

tecnologías AsP.net 4.0 (saltando desde la versión 2.0)

3.- dEFiNicióN dE lA PlANTillA dE ProducToS
ahora que disponemos de la infraestructura necesaria (servicio + Javascript) vamos a definir cómo queremos visualizar nuestra información. ello requiere que escribamos el hTML de la plantilla a mano, en el código de la página. el editor de visual studio nos ayudará con la sintaxis. en este caso usaremos una tabla tal y como vimos hace un momento en el apartado 1. Debemos marcar el elemento contenedor de nuestra plantilla para que luego asP.NeT aJaX sepa qué partes del hTML se deben repetir. La forma de hacerlo es ponerle un identificador cualquiera, y asignarle una clase css especial llamada “sys-template”, así en nuestro caso:
<tbody id=”Productos-template” class=”sys-template”>

es importante que el nombre de la clase css sea exactamente ese, “sys-template”, ya que es la forma que tiene aJaX de identificar a este tipo de elementos. Debemos asegurarnos de que nuestra página, internamente o en un archivo .css externo, define la clase de esta forma:
.sys-template { display:none; }

con ello conseguimos que la plantilla no se vea en la página aunque falle la generación dinámica de la tabla. si necesitamos utilizar otro estilo para el elemento (el estilo que normalmente tendría aplicado), podemos ponerlo también simplemente separándolo con un espacio, y se conservará tras el procesamiento:
<tbody id=”Productos-template” class=”miEstilo sys-template”>

Dentro de este elemento meteremos todo el hTML que queramos que se renderice para mostrar los datos, marcando la posición de éstos con una doble llave con el nombre del campo o propiedad del objeto que queremos visualizar. así, en nuestro ejemplo que devolvemos información de un producto sacado de la base de datos, la plantilla podría quedar así:
<tbody id=”Productos-template” class=”sys-template”> <tr> <td>{{ ProductName }}</td> <td>{{ UnitPrice }}</td>

</tbody>

</tr>

enlazado a datos en el navegador 95

4.- lA clASE dATAviEW
en estos momentos ya hemos decidido cómo vamos a visualizar la información, pero para que sirva de algo tenemos que indicar a aJaX cómo queremos utilizarla. La clase Sys.UI.DataView definida en la infraestructura de cliente de asP.NeT aJaX es la clave para activar las plantillas. Un DataView es un control de lado de cliente que nos permite relacionar un origen de datos y una plantilla para generar, de manera automática, el hTML necesario para visualizar la información. Los datos manejados por esta clase son muy variados: pueden ser matrices de objetos creadas en la propia página, datos resultantes de llamar a un servicio Web en el servidor o clases especializadas que ofrece aJaX para conectarse automáticamente a estos servicios Web (y que luego estudiaremos). cuando se le asigna una matriz o colección de objetos, el DataView actúa como un repetidor de la plantilla, generando una instancia por cada elemento de la matriz. se asemejaría a un control ListView o un Repeater de asP.NeT pero en el lado cliente. si por el contrario se le asigna un único objeto la plantilla sólo se instancia una vez y el control actúa como un visualizador de entidades (se parecería a un control de servidor DetailsView). existen tres maneras fundamentales de asociar datos a este control: 1. asociarle directamente una matriz de objetos o un objeto en su propiedad data. si, por ejemplo, tenemos una matriz de objetos ya definida en la propia página pues no se obtiene dinámicamente de un servicio externo. es la forma menos habitual. 2. Indicarle la dirección relativa de un servicio Web que proporciona los datos, para lo cual la propiedad adecuada es dataProvider. además en este caso es necesario indicarle mediante otra propiedad llamada fetchMethod el nombre de la operación del servicio que debe llamar para recuperar los datos. 3. Utilizar un objeto especializado para comunicarse con el servidor y usar éste como proveedor de datos para el DataView. se asigna el objeto especializado directamente a la propiedad dataProvider. estudiaremos estos objetos en un apartado posterior. existen dos maneras de instanciar el control DataView y asignarle los datos y la plantilla: por código y de manera declarativa. Para instanciarlo mediante código lo más sencillo es utilizar el método especial de asP.NeT aJaX llamado $create. el mejor sitio suele ser el evento init de la clase Application, y que se ejecuta al cargar la página. La sintaxis sería la siguiente:
Sys.Application.add_init(function() { $create( Sys.UI.DataView,

96

tecnologías AsP.net 4.0 (saltando desde la versión 2.0)

{

); });

}, null, null, $get(“Productos-template”)

data: “Servicios/NorthwindService.svc”, fetchOperation: “GetAllProducts”

La creación toma como parámetros el tipo de clase a crear, las propiedades del objeto que queramos establecer durante la creación, los manejadores de eventos que queramos asignar (en este caso ninguno, por eso se pasa un nulo), las referencias a otros componentes que se necesiten (ninguna en el caso de este control), y finalmente la plantilla a la que queremos asociar el DataView. ¡Buff!. Parece complicado pero no lo es tanto una vez que te acostumbras. De todas maneras lo más habitual es utilizar el control DataView de manera declarativa, es decir, sin necesidad de escribir código. esto es mucho más fácil y rápido, pero es importante conocer la técnica anterior (en código) por si fuera necesario en algunos casos complejos. Lo primero que necesitamos para poder usar el control declarativamente es indicar a la página nuestra intención, incluyendo el espacio de nombres apropiado dentro del cuerpo del hTML. Lo conseguimos con estos atributos:
<body xmlns:sys=”javascript:Sys” xmlns:dataview=”javascript:Sys.UI.DataView” sys:activate=”*”>

Lo que estamos declarando son dos espacios de nombres que nos servirán para escribir atributos especiales en los elementos hTML sin que el navegador “se queje”, y que la infraestructura de aJaX sepa interpretarlos. Los nombres después de xmlns son los prefijos para los atributos. así tendremos atributos sys y dataview. Puedes pensar en esto como en las directivas <@Register> de una página asPX que luego nos permiten usar controles de usuario en ellas. el atributo sys:activate sirve para indicar en qué controles queremos utilizar los diferentes atributos (y por tanto controles) que estamos definiendo. si ponemos un asterisco, como en el ejemplo, estamos indicando que los queremos usar con cualquier elemento de la página. La otra opción es utilizar una lista de identificadores de elementos separados por comas (por ejemplo: sys:activate=”productostemplate,txtNombre,txtApellidos”), que es más conveniente en páginas con mucho hTML ya que ofrece mejor rendimiento. al definir los espacios de nombres apropiados ya podemos asociar controles DataView a plantillas con atributos hTML directamente, sin código. Por ejemplo nuestra tabla de productos quedaría así:

enlazado a datos en el navegador 97

<tbody id=”Productos-template” class=”sys-template” sys:attach=”dataview” dataview:dataprovider=”Servicios/NorthwindService.svc” dataview:fetchoperation=”GetAllProducts” dataview:httpverb=”GET” dataview:autofetch=”true”> <tr> <td>{{ ProductName }}</td> <td>{{ UnitPrice }}</td> </tr> </tbody>

Fíjate que lo único que hay que hacer es adjuntar un control DataView a nuestro elemento contenedor de la plantilla gracias al atributo sys:attach. el resto son asignaciones de propiedades para que el nuevo control pueda funcionar, las cuales se establecen con el prefijo dataview definido antes, seguido del nombre de la propiedad y el valor adecuado para cada una. así, en este ejemplo lo que estamos haciendo con las propiedades es: • • • Indicar que el proveedor de los datos es el servicio Web ubicado en “servicios/Northwindservice.svc”, relativa a la página actual. Que el método que debe llamar para obtener los datos es “GetallProducts”. Que la llamada debe hacerla usando el método GeT, y no el método PosT que se usa por defecto. el motivo es que el servicio Web que hemos creado es de tipo resT (http://es.wikipedia.org/wiki/REST) y así lo requiere. Finalmente con la última propiedad le estamos diciendo que debe recoger automáticamente los datos y visualizarlos nada más crearse el control, para que no tengamos que forzarlo mediante código. así se obtendrán y enlazarán los datos nada más cargarse la página.

el orden en el que se asignen es indiferente. hacer esto es equivalente al código de inicialización que vimos antes, por lo que nos lo ahorramos y podemos hacer el enlazado de manera más sencilla y directamente en el hTML. recapitulemos un momento todo lo que hemos necesitado: 1. Definimos el hTML de la plantilla indicando donde van sus campos y marcando con la clase sys-template al elemento contenedor de la misma. 2. Declaramos en el cuerpo que queremos usar declarativamente el espacio de nombres Sys y el control Sys.UI.DataView, activando de paso los elementos que nos interesen. 3. activamos la plantilla asignando a su contenedor un control Dataview y estableciendo las propiedades necesarias.

98

tecnologías AsP.net 4.0 (saltando desde la versión 2.0)

como vemos es más sencillo y directo de lo que parece con todas las explicaciones intermedias, necesarias por otra parte. con esto ya tendríamos la funcionalidad deseada. si ahora ejecutamos la página veremos que se visualizan los datos de los productos de manera asíncrona tras cargar la página. Lo único que hemos transferido desde el servidor son la página original (prácticamente vacía) y los datos que necesitábamos mostrar. Todo lo demás se ha hecho en el cliente con Javascript.

Figura 2.- la tabla resultante generada en el cliente

De momento no es muy espectacular, pero en los siguientes apartados iremos mejorando paulatinamente el ejemplo a medida que aprendemos más características del enlazado de datos en el cliente.

5.- PSEudo-coluMNAS y ATribuToS ESPEciAlES
vamos a mejorar un poco la visualización de nuestra tabla de datos con varias características adicionales. en primer lugar vamos a hacer que las columnas pares tengan un color diferente a las columnas impares. con este objetivo vamos aprenderemos a usar las pseudo-columnas de datos. estas pseudo-columnas son campos especiales que se pueden utilizar en las plantillas y que nos ofrecen información de contexto sobre los datos que estamos enlazando. en concreto en la versión actual disponemos de las siguientes pseudocolumnas: • $index: nos indica el número (basado en cero) del registro que el DataView está procesando actualmente.

enlazado a datos en el navegador 99

$dataItem: se trata de una referencia al objeto de datos que se está usando en este momento para generar la plantilla, por lo que podremos usarlo para obtener información sobre el mismo con reflexión, llamar a alguno de sus métodos o lo que pudiésemos necesitar. $id(“identificador”): nos permite generar un identificador único para cada elemento de una plantilla, generado a partir de la cadena que se le pase. $element: permite el acceso al elemento de la plantilla que se está procesando.

• •

así, el primer añadido que vamos a hacer a nuestra tabla de datos es colocar una columna extra al principio de la misma que nos muestre el número de cada registro. Tras añadir la cabecera <th> correspondiente podemos incluir este código en la plantilla:
<td>{{ $index + 1 }}</td>

como $index está basado en cero le sumamos 1 para que tener una numeración más apropiada para el usuario. otra característica que necesitaremos son unos atributos especiales para plantillas llamados atributos class. antes de poder usarlos debemos declararlos en el cuerpo de modo análogo a como lo hicimos antes con los prefijos sys y dataview. La etiqueta del cuerpo quedaría así:
<body xmlns:sys=”javascript:Sys” xmlns:dataview=”javascript:Sys.UI.DataView” xmlns:class=”http://schemas.microsoft.com/aspnet/class” sys:activate=”*”>

a partir de este momento tenemos la capacidad de usar atributos class para establecer clases CSS condicionadas en los elementos de las plantillas. Lo que se consigue es asignar una clase css al elemento sólo en el caso de que se cumpla una determinada condición. en el ejemplo que nos ocupa vamos a usar una clase css para las columnas pares y otra diferente para las columnas impares, por lo que combinándolo con la pseudo-columna $index que acabamos de ver, la plantilla quedaría así:
<tr class:impares=”{{ $index % 2 != 0 }}” class:pares=”{{ $index % 2 == 0 }}”> <td>{{ $index + 1 }}</td> <td>{{ ProductName }}</td> <td>{{ UnitPrice }}</td> </tr>

100

tecnologías AsP.net 4.0 (saltando desde la versión 2.0)

Fíjate que ahora la fila de la tabla tiene un par de atributos class. el primero asigna la clase css de nombre “impares” a aquellas filas cuyo índice no sea divisible entre dos (es código Javascript normal y corriente que usa el operador % para calcular el módulo de una división), y asigna la clase “pares” en caso contrario. De esta forma conseguimos un aspecto diferente en filas alternas de la tabla.

6.- ATribuToS SyS coNdicioNAlES
Del mismo modo que podemos asignar clases de manera condicional, existen unos atributos especiales del espacio de nombres sys que trabajan de manera parecida. en este caso lo que nos permiten es establecer algunos atributos comunes de los elementos hTML. se trata de los siguientes: • sys:checked: se usa para generar el atributo checked de los elementos hTML que lo soportan, indicando una condición para ello. Lo usaremos ahora mismo en nuestro ejemplo. sys:disabled: idem que el anterior pero para el atributo disabled que permite desactivar controles. sys:id: se usa para generar identificadores de elementos mediante código Javascript. Por ejemplo, para generar un identificador a partir del identificador de producto podríamos escribir: sys:id=”{{ ‘Prod_’ + ProductID }}”. sys:src: para generar atributos src a partir de código. Muy útil por ejemplo para colocar rutas a imágenes sacadas de una base de datos.

• •

sabiendo esto vamos a añadir una columna más a nuestra plantilla para que se visualice el estado de disponibilidad de cada producto. hay un campo en la base de datos llamado Discontinued, que es verdadero si el producto está descatalogado. Podemos aprovecharlo para mostrar esta información usando un checkbox de hTML, mediante un atributo sys condicional del siguiente modo:
<td> <input type=”checkbox” disabled=”disabled” sys:checked=”{{

Discontinued }}” /> </td>

es decir, que sólo aparecerá marcado si el valor del campo Discontinued es true. con estos cambios nuestra nueva plantilla mostrará la lista de productos de la manera que se observa en la figura 3.

enlazado a datos en el navegador 101

Figura 3.- tabla resultante de nuestra plantilla mejorada.

7.- ATribuToS code PArA rENdEriZAdo coNdicioNAl
Una cuestión habitual al generar hTML en el cliente es que necesitemos que algunos elementos se creen sólo al cumplirse ciertas condiciones. Por ejemplo, al enlazar datos a una lista desplegable que actuará de filtro para otra tabla, si nos encontramos en el primer elemento que también se renderice un elemento adicional vacío que sirva para deshacer el filtro. este ejemplo en concreto lo tienes en el código del ZIP, en la página “Default.aspx”. Las variantes de este atributo son tres: • • code:if: si el resultado del código de este atributo es verdadero se renderizará el elemento, obviándolo de la interfaz en caso contrario. code:before: ejecutará, justo antes de procesar el elemento para la plantilla, el código Javascript que le asignemos como valor. Útil para hacer cosas durante el procesamiento que puedan afectar al resto de la plantilla. code:after: idem que el anterior, pero ejecutando el código al terminar de procesar el elemento actual.

estos atributos son muy útiles cuando los combinamos con las pseudo-columnas que estudiamos anteriormente.

102

tecnologías AsP.net 4.0 (saltando desde la versión 2.0)

Para poder utilizarlos hay que declarar su espacio de nombres en la etiqueta body, con lo que nos quedaría de la siguiente forma (ahora es el tercer espacio de nombres declarado):
<body xmlns:sys=”javascript:Sys” xmlns:dataview=”javascript:Sys.UI.DataView” xmlns:code=”http://schemas.microsoft.com/aspnet/code” xmlns:class=”http://schemas.microsoft.com/aspnet/class” sys:activate=”*”>

vamos a aprovechar estos atributos para añadir a nuestra tabla de datos una indicación visual sobre el stock actual de los productos. vamos a indicar tres niveles diferentes: verde, naranja y rojo, según si tenemos mucho stock, poco o ninguno en absoluto. el valor del inventario lo conocemos gracias al campo de la base de datos llamado UnitsInStock. añadiremos una nueva columna a la tabla que contendrá la definición de los tres niveles de stock con sendas imágenes:
<td> <img src=”Images/stock_ok.gif” code:if=” UnitsInStock >= 20 “ /> <img src=”Images/stock_m.gif” code:if=” UnitsInStock < 20 && UnitsInStock > 0” /> <img src=”Images/stock_ko.gif” code:if=” UnitsInStock == 0 “ /> </td>

hemos incluido en cada una de ellas un atributo code:if con una condición que define cuándo debe generarse el elemento en la plantilla. De este modo si el stock actual del producto es superior a 20 unidades, consideramos que tenemos mucho y se mostrará una marca de verificación de color verde. si es mejor de 20, indicaremos un stock mermado con una admiración naranja. Finalmente si no hay stock se indicará mediante un aspa roja.

Figura 4.- Nuestra tabla con indicadores de nivel de stock.

enlazado a datos en el navegador 103

estas expresiones nos dan mucho juego para determinar qué elementos queremos mostrar en cada momento y también para actuar sobre variables globales o sobre elementos que se van a procesar a continuación o que acaban de renderizarse.

8.- ENlAZAdo dE dAToS EN TiEMPo rEAl
hasta ahora hemos enlazado objetos usando la sintaxis de la doble llave {{ }}, conocida como evaluador de expresiones en línea. como deducimos de su nombre, su objetivo es evaluar las expresiones Javascript que hayamos escrito dentro. Una vez hecho esto no hay ningún tipo de relación posterior con los elementos subyacentes. De hecho podemos utilizarlas para evaluar cualquier tipo de expresión Javascript, no es necesario que sea una propiedad del objeto que estamos enlazando. el uso de esta sintaxis nos otorga un gran poder, pero ofrece una limitación fundamental: el enlace se realiza sólo una vez, al procesar la plantilla. Una vez visualizada la información, si se produce cualquier cambio en los datos subyacentes, éste no se verá reflejado en la interfaz. La funcionalidad de plantillas de asP.NeT aJaX permite mantener sincronizados, en tiempo real, los datos enlazados y la interfaz de usuario. De esta manera si la información varía (porque la hemos editado en otro lugar de la página, por ejemplo), sus nuevos valores se ven reflejados de inmediato en todas las plantillas que los usen. esta funcionalidad se basa en el uso del patrón de diseño Observer, gran conocido por los programadores aficionados a la arquitectura de software, y en especial los que vienen del mundo Java.
Nota:
No vamos a entrar en detalle aquí sobre el funcionamiento de este patrón, pero baste decir que es suficiente utilizar una llamada al método makeObservable de la clase Sys.Observer (incluido por las extensiones de Javascript de asP.NeT aJaX), para que automáticamente nuestro objeto genere eventos ante todos los cambios que se produzcan en sus propiedades. Y esto es independiente de la funcionalidad de plantillas por lo que podemos sacarle partido en nuestros propios desarrollos para otras cosas.

el enlazado a datos en las plantillas tiene una sintaxis alternativa que habilita el refresco en tiempo real de la interfaz. en lugar de usar una doble llave, se emplea una llave simple junto con la palabra clave binding. es decir, en lugar de escribir:
{{ ProductName }}

escribiremos
{binding ProductName }

y automáticamente, a partir de ese momento, si cambia el valor del nombre de producto, este cambio se verá reflejado automáticamente en la interfaz generada a partir de la plantilla. Y todo esto sin tener que escribir ni una línea de código. esta característica se denomina Live Binding o Enlazado en Tiempo Real.

104

tecnologías AsP.net 4.0 (saltando desde la versión 2.0)

Dependiendo del elemento hTML al que enlacemos los datos, el enlace puede ser en un solo sentido (single-way) o bidireccional (two-way). Por ejemplo si enlazamos un campo a un control input de texto, lo que escribamos en éste hará que se modifique el valor subyacente, modificando los datos originales. sería un enlace bidireccional, puesto que los cambios en el objeto se reflejan en el control y viceversa. sin embargo si lo enlazamos a un elemento de sólo lectura (por ejemplo a un <span>), el enlace sólo será posible en un sentido, ya que el elemento al no cambiar no puede afectar a los datos originales. Todo esto nos resultará de gran ayuda para enviar información modificada al servidor como veremos enseguida. Mientras tanto vamos a ver un ejemplo rápido de uso que, si bien no será muy útil, nos dará una idea precisa de cómo funciona el Live Binding. Para ello enlazaremos una plantilla a un elemento de la página, y no a un origen de datos. así también veremos cómo se puede usar para muchas más cosas que para enlazarnos a datos traídos desde el servidor.
Nota:
este ejemplo lo tienes en las descargas del libro, dentro de la carpeta correspondiente a las plantillas de datos (vs2010_PlantillasDatoscliente_aJaX), en el archivo llamado “BindingenDossentidos_ejBasico.aspx”.

arrastremos un control de texto hTML (<input>) a la página. a continuación, dentro de un <span> que actuará de contenedor de nuestra plantilla, definiremos otro <span> y un nuevo control de texto. La sintaxis final será esta:
<input type=”text” id=”txtOrigen” value=”” />&nbsp; <span id=”textoEnlazado” class=”sys-template” sys:attach=”dataview” dataview:data=”{{ $get(‘txtOrigen’) }}”> <span>{binding value}</span>&nbsp; <input type=”text” id=”txtCopia” value=”{binding value}” </span>

/>

en este caso usamos el dataView para enlazarnos directamente contra el primer cuadro de texto. aquí no hay datos traídos del servidor ni código alguno, sólo sintaxis declarativa. Fíjate que no hay tampoco código de servidor, y que podríamos haber usado una página con extensión .htm siempre que incluyamos los scripts de asP.NeT aJaX como archivos externos y no con un ScriptManager. en la parte correspondiente a la plantilla hemos usado Live Binding para mostrar el valor del campo de texto (propiedad value de éste, por eso se escribe

enlazado a datos en el navegador 105

{ binding value }) en una etiqueta (<span>) y en otro cuadro de texto. ¿Qué ocurrirá al ejecutarlo? abre la página y juega con ella un poco. verás que todo lo que escribas en el primer cuadro de texto se verá automáticamente reflejado en los otros dos elementos en cuanto quites el foco de él. al mismo tiempo, como el segundo cuadro de texto tiene un enlace bidireccional con el primero, al escribir algo en él y quitarle el foco, el cambio aparecerá de inmediato en el primero y en la etiqueta. en la práctica lo que hemos conseguido es mantener sincronizados ambos cuadros de texto y la etiqueta. Fíjate además que la propia infraestructura de aJaX se ocupa de evitar que entremos en un bucle de cambios infinitos cuando uno cambia al otro. esta funcionalidad nos otorga un enorme poder para el manejo de datos en el cliente, y en los próximos apartados vamos a ver cómo sacarle todo el partido posible.

9.- viSTAS MAESTro-dETAllE: PrEPArAr El MAESTro
vamos a seguir mejorando nuestro ejemplo para, al mismo tiempo, aprender nuevas características de las plantillas de lado cliente. en esta ocasión vamos a añadirle la posibilidad de mostrar un detalle de los registros al pulsar sobre ellos. se trata de la típica vista maestro-detalle que hay en casi todas las aplicaciones: facturas-líneas de factura, proveedor-artículos, etc... en esta ocasión vamos a mostrar en otro lugar de la página, con una nueva plantilla, los datos seleccionados en nuestra lista de productos. Lo primero que vamos a ver es de qué manera podemos provocar la selección. existen tres atributos especiales del espacio de nombres Sys que están especializados en generar acciones para los elementos de las plantillas. se trata de: • sys:command: el nombre del comando que queremos lanzar. actualmente el único comando estándar existente es “select” para seleccionar elementos. Podemos, no obstante, emplear cualquier otra cadena arbitraria para definir comandos propios, y así generar eventos adaptados a nuestras necesidades. sys:commandargument: argumentos que queremos pasarle al comando, para particularizarlo. Quizá un identificador, una referencia a un elemento, etc... sys:commandtarget: el elemento sobre el que queremos actuar. Normalmente no se utiliza.

• •

en nuestro ejemplo vamos a hacer que se seleccione el producto actual en el momento en el que el usuario pulse sobre cualquier punto de fila de la tabla de productos. redefinimos la fila (elemento <tr> en nuestro ejemplo) añadiéndole el atributo sys:command, así
<tr sys:command=”Select” .... />

106

tecnologías AsP.net 4.0 (saltando desde la versión 2.0)

esto provoca que el procesador de plantillas incluya el Javascript necesario para conseguir ese efecto. el evento de selección es automáticamente reconocido por el control dataView. si ahora probamos el ejemplo, lo cierto es que no notaremos diferencia alguna con lo que ya teníamos. Necesitamos algo más para que se manifieste visiblemente la selección. el control dataView está diseñado para trabajar en equipo con sys:command, y dispone de una propiedad llamada selectedItemClass. Ésta sirve para asignar automáticamente un estilo css al elemento que provoca el comando. Define en la página del ejemplo una nueva clase css, de nombre “.seleccionado”, que establezca el color de fondo en un tono que destaque sobre los restantes utilizados en la página. ahora añade este atributo al elemento contenedor de la plantilla:
dataview:selecteditemclass=”seleccionado”

ejecuta de nuevo la aplicación. ahora sí que seleccionarás de manera visible cada fila, ya que al hacer clic sobre una de ellas ésta adoptará el estilo de nombre “seleccionado”. Los datos que queremos visualizar como vista de detalle de nuestros productos son los campos del producto actualmente seleccionado. Los dataView tienen una propiedad llamada selectedData que devuelve precisamente esta información. Necesitamos alguna manera de identificar al dataView de los productos para poder acceder a sus propiedades y así poder leer los datos del elemento seleccionado. aquí es donde entra el último atributo que veremos dentro del espacio de nombres de sistema: sys:key. con él podemos asignar de manera declarativa identificadores Javascript a los componentes, como el dataView, que activemos en los elementos hTML. Para otorgar un nombre a nuestro dataView de productos añadiremos el siguiente atributo al elemento contenedor de la plantilla de productos (míralo en el código de ejemplo):
dataview:sys-key=”TablaProductos”

De este modo a partir de ahora podremos acceder a este dataview desde otros elementos de plantillas utilizando el identificador que le hemos asignado, en este caso “TablaProductos”. Fíjate en que como se está aplicando un sys:key como atributo secundario de dataView, se usa un guión y no dos puntos en la declaración anterior: dataview:sys-key.

10.- viSTAS MAESTro-dETAllE: PrEPArAr loS dETAllES
recapitulemos lo que hemos hecho hasta ahora: • hemos usado sys:command para indicar qué elemento hará la selección.

enlazado a datos en el navegador 107

• •

establecemos el estilo de los elementos seleccionados con la propiedad selectedItemClass 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 contenidos 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 necesidades. 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).

108

tecnologías AsP.net 4.0 (saltando desde la versión 2.0)

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 detalles, 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!.
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 sabemos, no habilitan el Live binding.

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-

enlazado a datos en el navegador 109

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 cambios 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 inicializació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.

110

tecnologías AsP.net 4.0 (saltando desde la versión 2.0)

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 creado 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. colocaremos 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.
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 detectar 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”

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 encontrar por Internet usan la clase AdoNetDataContext y un servicio autogenerado de

enlazado a datos en el navegador 111

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.

112

tecnologías AsP.net 4.0 (saltando desde la versión 2.0)

Figura 7.- código del método de actualización

como vemos toma como parámetro una lista genérica de objetos Change particularizados 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 resultado (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.

13.- hiSToriA dEl NAvEgAdor
La principal ventaja de las interfaces aJaX es que todo se produce, aparentemente, en el navegador y eliminamos los refrescos de página. sin embargo esta ventaja en ocasiones se puede convertir en un problema. consideremos por ejemplo la página de productos que hemos estado desarrollando en este capítulo, e imaginemos que sirve para mostrar la información detallada de cada producto a los visitantes de nuestra Web. cuando el usuario pulsa sobre un producto se visualizan sus detalles, pero como todo se hace desde el lado del cliente, de repente nos damos cuenta de que hemos perdido dos cosas muy importantes: la capacidad de usar el botón de volver hacia atrás en el navegador, y la capacidad de que el usuario guarde en sus favoritos (o envíe a un amigo) un producto concreto en el que esté interesado. Imagina que eres un cliente que está buscando un producto en la página y que has estando seleccionando varios para ver sus características. De repente deseas volver a uno de los anteriores que ya habías visto y al darle al botón “atrás” del navegador descubres que éste no hace nada o, lo que es peor, si habías estado antes

enlazado a datos en el navegador 113

en otra página ésta aparece sustituyendo a la actual. Le vuelves a dar hacia adelante y regresas a la página en la que estabas, pero todo se carga de nuevo y no tienes forma de saber en qué producto estabas. No parece una situación muy deseable ¿verdad? ahora llegas a un producto que te interesa y decides enviárselo a un amigo para que le eche un vistazo y te dé su opinión. copias y pegas la UrL de la página y se la mandas por correo electrónico. Lo malo es que como la página es la misma para todos los productos (gracias a aJaX), al abrir la otra persona el enlace en su navegador se encuentra con la lista de productos, pero ninguno seleccionado, por lo que no tiene forma de saber a cuál de ellos te estabas refiriendo. como ves, no son problemas tan banales como parecen a simple vista. ¿cómo podemos solucionarlos? Desde la aparición del service Pack 1 de .NeT 3.5, está disponible una nueva característica destinada a atajar este problema. se trata de la propiedad EnableHistory de la clase ScriptManager. cuando la establecemos a True en una página, automáticamente disponemos, tanto en el cliente como en el servidor, de un método AddHistoryPoint que nos permite añadir entradas a la historia del navegador, y un evento Navigate, que se llama automáticamente cuando el usuario pulsa los botones de navegación para moverse por los puntos que hemos definido. estos dos miembros del ScriptManager se usan en eventos de servidor para crear y restaurar el estado de la página cuando trabajamos con controles UpdatePanel. en el lado cliente debemos decidir en qué momentos queremos añadir entradas a la historia y cómo vamos a actuar cuando se intente navegar por ellas. en realidad la parte de servidor lo que hace es enviar al navegador el mismo código que usaríamos nosotros para hacerlo desde Javascript. vamos a retocar nuestro ejemplo del listado de productos para que, cada vez que seleccionemos un producto, se cree una entrada en el historial de navegación. veremos que, de este modo, habilitaremos tanto la historia como el poder crear enlaces directos a productos seleccionados. Lo primero es establecer la propiedad EnableHistory del ScriptManager como True. ahora ya podremos utilizar toda la funcionalidad necesaria. vamos a capturar el evento de selección de un producto en la tabla, para lo cual añadimos el siguiente atributo al dataView asociado con ésta:
dataview:oncommand=”{{ gestionarSelect }}”

ahora, cada vez que se seleccione una columna se llamará a esta función de Javascript que tendremos que definir. además añadiremos al comando Select alguna información de contexto que nos ayude a distinguir en qué fila se ha producido. aquí viene muy bien el atributo sys:commandarguments que hemos estudiado hace un rato:
<tr sys:command=”Select” sys:commandargument=”{{ { Fila: $index, Nombre: ProductName} }}” ... >

114

tecnologías AsP.net 4.0 (saltando desde la versión 2.0)

Lo que hacemos es pasar como argumento al comando de selección un objeto anónimo (en formato JsoN) que tiene dos propiedades: • • Fila: que indica en qué fila estamos actualmente. Nombre: el nombre completo del producto, que nos servirá para hacer las entradas del navegador un poco más amigables.

este objeto se recibirá en la función que vamos a definir para gestionar el evento de selección, y que tendrá este aspecto:
function gestionarSelect(sender, args) { if (args._commandName.toLowerCase() == “select”) { var infoProd = args._commandArgument; Sys.Application.addHistoryPoint({ FilaSeleccionada: infoProd. Fila }, “Detalles del producto - “ + infoProd.Nombre); } }

como podríamos compartir esta función entre varios comandos lo primero es comprobar si se trata de un comando de selección, que es el que nos interesa. La propiedad _commandArgument del segundo parámetro sirve para recibir el objeto JsoN definido como argumento en la plantilla que, si recordamos, nos informaba de la fila seleccionada y del nombre del producto. el método addHistoryPoint crea la entrada en la historia del navegador. Los parámetros que le debemos pasar son un objeto arbitrario con información sobre el estado a restaurar posteriormente, y una descripción para el punto de navegación. en este caso le pasamos un objeto JsoN que sólo tiene una propiedad en la que se guarda la fila seleccionada por el usuario, pero le podríamos incluir toda la información adicional que quisiésemos. Menos es más, de todos modos, así que si puede ser sólo un número o algo así, mucho mejor pues las UrLs generadas quedarán más cortas. ahora sólo nos queda responder al evento de navegación que se produce cuando el usuario usa el botón de adelante o atrás en el navegador, o cuando pegue la UrL generada en la barra de direcciones. Para definir la función que gestionará el evento usaremos el método add_navigate de la clase Sys.Application:
Sys.Application.add_navigate( function(sender, e) { var fila = parseInt(e.get_state()[“FilaSeleccionada”], 10); if (fila != NaN) { $find(“Productos-template”).set_selectedIndex(fila); } } );

enlazado a datos en el navegador 115

Lo que hacemos es obtener el valor de la fila actual a partir del estado que hemos enviado como argumento en el paso anterior. recuerda que sólo mandábamos la fila actualmente seleccionada. si todo ha ido correcto lo que hacemos es indicarle al dataView que gestiona nuestra tabla de productos que esa debe ser la fila que seleccione. Ya está. si ahora probamos nuestra aplicación veremos que se añaden entradas en el historial de navegación y que podemos movernos por ellas sin problemas a pesar de que la página no se recarga ni se envía de nuevo al servidor.

Figura 8.- historial de navegación AJAX

Fíjate además en cómo se ha serializado la información sobre el estado actual de la página, directamente en la UrL, justo a continuación del carácter #. esto permitirá a los usuarios pegar directamente esta dirección en su navegador y obtener la página con el elemento apropiado ya seleccionado. como efecto secundario, además, obtenemos un título descriptivo para la página, que coincide con el texto de la entrada de navegación, obteniendo descripciones más informativas sobre lo que estamos visualizando.

14.- EN rESuMEN
en estos dos últimos capítulos hemos aprendido las técnicas más importantes que nos ofrece asP.NeT aJaX para trabajar en el lado de cliente. La mayor parte de los programadores cuando descubren asP.NeT aJaX se quedan únicamente con los controles de servidor, y en especial el control UpdatePanel, y dejan de lado el trabajo en el lado cliente y la visión de aJaX “puro” que he descrito aquí. se trata de un error. Gracias a todas las técnicas aprendidas estarás en condiciones de escribir aplicaciones Web mucho más ligeras, escalables y con mayor velocidad de respuesta para los usuarios. No dejes de lado los controles de servidor, pero ten siempre en cuenta que Javascript y todas las extensiones de este lenguaje que brinda la asP.NeT, te podrán proporcionar un gran poder de desarrollo.

116

tecnologías AsP.net 4.0 (saltando desde la versión 2.0)

en el ejemplo de las plantillas de lado cliente que encontrarás en el ZIP de los ejemplos del libro, he incluido algunas cosas interesantes a mayores de lo que hay en este capítulo. Todas están comentadas en el código fuente y puedes investigarlas por tu cuenta. en el archivo “Default.aspx” verás un enlazado a datos algo diferente, en el que se utiliza una lista desplegable para obtener la lista de proveedores de “Northwind” y luego, en función de la selección, mostrar qué productos ofrecen en un formato maestro-detalle como el que hemos estudiado. en el archivo “BindingPorcodigo.aspx” también se ha modificado el campo de precio de producto para que lo muestre con el símbolo del euro y multiplicado por 100 usando una forma especial de enlazado. Te servirá para aprender la forma de modificar un valor antes de mostrarlo, usando la sintaxis {binding Campo, convert=Funcion, convertBack=funcion}, similar al enlazado a datos que se utiliza en WPF (Windows Presentation Foundation).

cAPíTulo

AsP.net Dynamic Data: interfaces de datos a la velocidad de la luz

6

en las aplicaciones web, como en casi todos los tipos de aplicaciones, una de las tareas que más tiempo consume y que más aburre a los programadores es la de crear interfaces de gestión. se trata de todas esas anodinas pantallas en las que invertimos tantas horas y que sirven básicamente para dar altas, bajas y modificar registros en una interminable lista de tablas de la base de datos. son, para que nos entendamos, los famosos “mantenimientos”. Gracias a asP.NeT y todos sus controles enlazados a datos crear estas páginas es bastante simple, y las podemos hacer casi sin escribir código. aún así no queda más remedio que crearlas una a una, lo que puede llevar muchas horas de trabajo incluso con las facilidades que tenemos. ¿No sería fantástico si pudiésemos conseguir que se crearan solas? esto es precisamente lo que nos proporciona ASP.NET Dynamic Data. antes de continuar, unas palabras de advertencia para los escépticos: automático no quiere decir rígido e inflexible. Uno de los miedos atávicos que tienen los programadores que llevan unos cuantos años en esto es, sin duda, a la generación automática de código. Lo que les suele provocar recelo son dos cosas: la pérdida de control y la merma de rendimiento, que en el fondo son lo mismo. el no saber lo que pasa por debajo es frustrante y además tienden a pensar que, en un momento dado, les va a limitar para que la aplicación crezca o cuando necesiten algo muy a medida de sus necesidades. Nada más lejos de la realidad en el caso de Dynamic Data. como veremos se trata de un modelo absolutamente flexible y extensible, sin que ello haga que pierda sus enormes ventajas en cuanto a productividad. Incluso si esto no te convence tengo una buena noticia: ni siquiera tendrás que usar la generación dinámica de la interfaz de usuario si no quieres. aún así le podrás sacar partido a esta tecnología desde tus interfaces “normales”, creadas a partir de controles estándar como las rejillas y otros :-) Dicho esto, vamos a continuar con el estudio de esta interesante tecnología que va a multiplicar tu productividad.

117

118

tecnologías AsP.net 4.0 (saltando desde la versión 2.0)

1.- ¿Qué ES dyNAMic dATA?
La primera vez que oí hablar de esta tecnología —y tuve la suerte de verla en funcionamiento— fue en seattle, en las instalaciones de Microsoft a principios de 2007. entonces el nombre interno era “oryx” y me dejó impresionado por la productividad que prometía dar. al final tuvo que pasar bastante tiempo para que realmente estuviera disponible y se pudieran hacer las primeras pruebas con ella. La tecnología definitiva, que es la que nos ocupa, apareció con el service Pack 1 de .NeT 3.5 en octubre de 2008. en asP.NeT 4.0 se han introducido algunas mejoras para ganar en flexibilidad.
Nota:
Todo lo explicado en este capítulo es utilizable tanto en asP.NeT 3.5 sP1 como en asP.NeT 4.0. en los sitios que hay alguna diferencia se indica mediante una nota. en el archivo ZIP con los ejemplos del libro se facilitan los ejemplos en ambas versiones del entorno de desarrollo para tu comodidad. Por eso verás también que hay muchas notas como esta, más que en cualquier otro capítulo del libro.

Dynamic Data permite crear de manera dinámica interfaces de gestión de datos para nuestras aplicaciones. se basa en un mecanismo de “andamiaje” que permite a asP.NeT construir páginas al vuelo para gestión de entidades, a partir de la UrL que le sea solicitada, sin tener que escribir código alguno para ello.
Nota:
el término “andamiaje” se aplica aquí porque procede del vocablo equivalente en inglés “scaffolding” que se utiliza mucho al referirse a la creación automática de interfaces. La palabra se popularizó a partir del año 2005 con la aparición del framework llamado Ruby On Rails, que fue pionero en estos conceptos, atados por regla general al Modelo vista-controlador (Mvc). La idea es que la infraestructura proporciona automáticamente un andamiaje inicial para la interfaz de acceso a datos, y el programador la particulariza para adaptarla a sus necesidades.

Dynamic Data sustenta su funcionamiento básicamente en tres pilares: • Modelo de datos: se utiliza un modelo basado en objetos para representar a los datos con los que vamos a interactuar. este modelo define cómo son estos datos, cuáles son sus relaciones y sus requerimientos, pero también se usa para leer y escribir en el almacén de información subyacente. el modelo define además las validaciones que se harán sobre la información introducida por los usuarios. Plantillas: existen unas plantillas de página, plantillas de entidades y plantillas de campos de datos que son las que se utilizan para generar las interfaces. asP.NeT nos proporciona multitud de ellas por defecto, y podemos crear las nuestras personales para particularizar comportamientos.

AsP.net Dynamic Data: interfaces de datos a la velocidad de la luz 119

Rutas: dado que las páginas se generan dinámicamente y no existen como archivos físicos en la realidad, las rutas en la UrL de la aplicación adquieren una importancia esencial, ya que son la forma de comunicar a la aplicación qué páginas generar y con qué propósito.

el modelo se construye usando un diagrama de entidades orM (Object-Relational Mapping). Éste representa mediante un conjunto de objetos a las tablas de una base de datos relacional. en la actualidad existen dos formas soportadas de construir estos modelos de datos: Linq2SQL y Entity Framework. La primera es una tecnología de acceso a datos aparecida con .NeT 3.5, y la siguiente, más evolucionada y compleja, apareció con el service Pack 1 de .NeT 3.5. ambas tecnologías permiten crear de manera visual y sencilla un modelo de datos para interactuar con la base de datos de modo orientado a objetos. se manejan objetos en lugar de tablas, y se usan expresiones Linq en lugar de consultas sQL. ambos ofrecen grandes ventajas de productividad y mantenimiento sobre los modelos tradicionales.
Nota:

En Krasis Press existen libros dedicados a ambas tecnologías escritos por los principales expertos en la materia. Es prácticamente la única literatura en castellano existente sobre el tema. No te olvides de visitar la Web www.krasispress.com para informarte si estás interesado en Linq o EF.

Una vez que se crea un modelo de datos visualmente, basta con registrarlo en Dynamic Data para obtener un sitio web de mantenimiento plenamente funcional y con múltiples características ya añadidas (como el filtrado de datos, por ejemplo). Los controles de datos GridView y DetailsView han sido modificados para soportar campos dinámicos de Dynamic Data. además otros controles, como el ListView o el FormView soportan una funcionalidad similar gracias a un nuevo control denominado DynamicControl. otro aspecto que ha mejorado en asP.NeT gracias a esta tecnología es la validación de la entrada de información, ya que es posible conseguir validación automática de los datos basándose únicamente en el modelo y sus restricciones.

2.- NuESTro PriMEr ProyEcTo coN dyNAMic dATA
Podemos emplear esta tecnología en cualquier aplicación Web, pero lo mejor para sacarle todo el jugo es partir de una infraestructura previa proporcionada por una plantilla de proyecto especial. si abres visual studio y creas un nuevo sitio Web o aplicación Web verás que existen dos tipos de proyecto asociados a Dynamic Data, como se observa al final de la lista de la figura 1.

120

tecnologías AsP.net 4.0 (saltando desde la versión 2.0)

Figura 1.- Proyectos Web disponibles en visual Studio 2010

Nota:
estos tipos de proyectos están disponibles a partir de visual studio 2008 con service Pack 1. si no te aparecen en el diálogo de nuevo sitio Web instala como mínimo esta versión. La tecnología está soportada también en las versiones gratuitas de visual studio (express).

cada tipo de proyecto se refiere a una de las dos tecnologías disponibles para crear el modelo de datos, por lo que debemos escoger uno u otro según queramos usar Linq2SQL o Entity Framework respectivamente. Para los propósitos de este libro vamos a utilizar la versión para Linq, pero no hay apenas diferencia y es sólo una decisión respecto a con qué tecnología estemos más cómodos. Lo primero que te llamará la atención al crear uno de estos proyectos es que ya existen de entrada varias carpetas y bastantes elementos (páginas asPX y controles) creados por la plantilla. Luego los examinaremos con detalle, pero baste decir que se trata de las plantillas base para generar la interfaz de usuario, y que podremos modificarlas para adaptarlas a nuestras necesidades tanto como sea necesario. De momento haz caso omiso de ellas.

AsP.net Dynamic Data: interfaces de datos a la velocidad de la luz 121

3.- dEFiNir El ModElo dE dAToS
Lo primero que debemos hacer es añadir un modelo de datos a nuestro proyecto. como hemos comentado éste se encargará de indicar a la infraestructura de generación de páginas cómo son los datos con los que vamos a trabajar: qué tablas hay, sus relaciones, las restricciones en los campos de datos, etc... en este ejemplo vamos a usar nuevamente la archiconocida base de datos “Northwind” de sQL server. en el explorador de soluciones pulsa con el botón derecho sobre el nodo del proyecto y añade un nuevo elemento. en el diálogo escoge la opción “clase Linq to sQL”. Llámale Northwind.dbml y acepta. cuando te indique que debes añadir la carpeta App_Code al proyecto, di que sí, puesto que es necesaria para albergar este nuevo archivo. Desde el explorador de servidores de visual studio añade una conexión a la base de datos Northwind. Despliega el nodo correspondiente para mostrar sus tablas y arrastra desde allí sobre la superficie del diseñador de Linq2sQL las que quieras utilizar en el proyecto. Yo he empleado las de categorías, productos, proveedores, clientes, pedidos y detalles de pedido, como se muestra en la figura 2.

Figura 2.- las tablas a utilizar seleccionadas en el explorador de servidores

Tras haberlas arrastrado el modelo quedará definido en la superficie de diseño y sólo tendrás que grabar el resultado para que todo esté listo para trabajar. en la figura 3 se observa el aspecto del diseñador con las tablas ya añadidas.

122

tecnologías AsP.net 4.0 (saltando desde la versión 2.0)

Figura 3.- El modelo de datos inicial con linq2SQl.

4.- AñAdiENdo El ModElo A dyNAMic dATA
ahora abre el archivo Global.asax de la aplicación. al hacerlo verás que contiene un evento llamado RegisterRoutes. su código comienza con un gran comentario en el que se nos indica que para poder usar Dynamic Data con un modelo de datos primero debemos registrarlo con el método RegisterContext de la clase MetaModel. esta clase se ocupa de registrar los modelos de datos para que sean utilizados por Dynamic Data para la generación de interfaces y controles. Descomenta la última línea de ese bloque de comentarios y utiliza nuestro recién creado contexto de datos (NortwindDataContext) como parámetro para el método:
DefaultModel.RegisterContext(GetType(NorthwindDataContext), New ContextConfiguration() With {.ScaffoldAllTables = True})

Fíjate en que el último atributo, ScaffoldAllTables lo hemos cambiado también para que valga True en lugar de su valor original que era False. con esto lo que le estamos indicando a la infraestructura de Dynamic Data es que debe generar páginas para todas las tablas de nuestro modelo.
Nota:
Para los propósitos de este ejemplo esto es lo que queremos. Más adelante veremos que no es necesario usar esta parte de la tecnología si preferimos crear nuestras páginas de una manera manual. en cualquier caso si lo habilitamos deberemos asegurarnos de que hemos protegido adecuadamente el acceso a las rutas de administración autogeneradas, o si no estaríamos abriendo el acceso a manejar las tablas a cualquiera. así que ¡atención a esto!.

AsP.net Dynamic Data: interfaces de datos a la velocidad de la luz 123

Pulsa F5 para ejecutar la aplicación. verás que automáticamente dispones de una página inicial con una lista de tablas en el modelo y que puedes navegar por ellas.

Figura 4.- vista previa de la interfaz de gestión generada por defecto

si usas los enlaces verás que se ha creado una interfaz básica para gestionar todas y cada una de las tablas que teníamos en el modelo. Para cada tabla se muestran todos los campos disponibles (luego veremos cómo decidir cuáles mostrar), y podemos editar los registros, borrarlos, ordenarlos por cualquier columna e incluso añadir nuevos registros. La tecnología es lo suficientemente inteligente como para mostrarnos las entidades relacionadas entre sí usando elementos apropiados para el usuario final. así, por ejemplo, al editar un producto en el campo de proveedor obtenemos una lista con los nombres de los proveedores y no tenemos que introducir el valor numérico correspondiente a la clave externa, lo cual sería poco operativo en una interfaz de administración. También se crean filtros para poder acotar los listados a partir de otras entidades relacionadas y campos boolenaos. en la figura 4 se puede intuir a qué me refiero, y vemos que en la parte de arriba aparece la lista de categorías de productos desplegada, para que podamos filtrar por ella. Los filtrados se hacen al estilo aJaX, es decir, sin recarga de la página. aunque así todavía no es suficiente, no está mal para sólo haber arrastrado unas tablas y descomentado una línea ¿verdad?.

124

tecnologías AsP.net 4.0 (saltando desde la versión 2.0)

5.- PlANTillAS dE iNTErFAZ dE uSuArio
observemos someramente los elementos presentes en el proyecto para ver cómo funciona el proceso por debajo. La primera página que podemos ver es “Default. aspx”. en realidad no tenemos porqué usarla, pero se proporciona para tener un punto de partida desde el que acceder a las páginas auto-generadas. Lo único que hace esta página es mostrar la lista de tablas disponibles con un enlace a la página de listado de cada una de ellas. si nos sabemos las rutas a las páginas (más sobre esto luego), no nos hace falta en absoluto. Échale un vistazo no obstante al código del evento Load de la página para ver cómo enlaza el modelo a una rejilla para sacar el listado de las tablas: nunca se sabe cuando lo puedes necesitar.
Nota:
en asP.NeT 4.0 se generan los enlaces con un nuevo control especial llamado DynamicHyperLink. en la versión 3.5sP1 se usan enlaces normales.

Lo realmente interesante es la carpeta DynamicData existente en la raíz de la aplicación. En ella vemos que hay varias subcarpetas llenas de páginas y controles.

Figura 5.- La carpeta Dynamic Data

AsP.net Dynamic Data: interfaces de datos a la velocidad de la luz 125

La subcarpeta PageTemplates contiene una serie de páginas asPX que serán las que se utilicen como plantillas para la interfaz de gestión que hemos visto, generada automáticamente por Dynamic Data: • • • • • List.aspx: genera la página principal, que lista todos los registros de la tabla y brinda acceso a las demás páginas: edición, inserción o detalles. Details.aspx: muestra los detalles de una entidad. Edit.aspx: genera un interfaz que permite editar cualquier entidad/tabla. Insert.aspx: la plantilla para insertar un nuevo registro. ListDetails.aspx: esta es la plantilla más completa de todas, puesto que aúna en una sola página las funcionalidades de las otras cuatro. Útil si queremos tener todo junto.

si las examinamos veremos que son páginas asPX normales, y que apenas contienen código. Podemos modificarlas a nuestro antojo para adaptarlas a lo que necesitemos. cualquier cambio o añadido que hagamos en ellas se presentará en la interfaz autogenerada en tiempo de ejecución. Todas ellas utilizan como Master Page a Site.master, ubicada en la raíz de la aplicación, por lo que modificando ésta afectaremos a la disposición de elementos en todas las plantillas. Las cinco páginas se parecen mucho, así que vamos a examinar una de ellas para ver su estructura y comprender mejor su funcionamiento.

5.1.- diseccionando una plantilla
Por ejemplo, vamos a analizar “List.aspx”, la página que permite listar los registros de cualquier tabla de datos. Lo primero que examinaremos será su código, contenido en List.aspx.vb o List. aspx.cs según uses vB o c# como lenguaje de desarrollo. el código define un variable protegida de ámbito de página llamada table y de tipo MetaTable. esta clase System.Web.DynamicData.Metatable encapsula los metadatos de una tabla/entidad que está siendo manejada por Dynamic Data. a través de sus miembros tenemos acceso a la información sobre el modelo de datos subyacente. así, por ejemplo, podemos averiguar el nombre de la tabla con su propiedad DisplayName, o conocer los detalles de sus campos a través de su colección Columns. La referencia completa la tienes aquí: http://msdn.microsoft.com/es-es/library/system.web.dynamicdata.metatable_members.aspx en el evento Load de la página/plantilla se asigna a este miembro, table, los metadatos de la tabla actualmente manejada en la plantilla, de forma que se pueda

126

tecnologías AsP.net 4.0 (saltando desde la versión 2.0)

hacer uso de ella desde cualquier parte de la página para mostrar información sobre la entidad gestionada. en la versión 3.5 sP1 el código de asignación es este:
table = GridDataSource.GetTable

GridDataSource es como se llama el control LinqDataSource o EntityDataSource (según la tecnología de datos que hayamos elegido) que se encarga de hacer el acceso a los datos en la plantilla. estos controles han sido extendidos con algunas propiedades y métodos como este (GetTable) para dar soporte a Dynamic Data. en asP.NeT 4.0 el código equivalente utilizado es el siguiente:
table = GridQueryExtender.SetTableFromRoute

en ese caso se usa el método SetTableFromRoute de un control QueryExtender incluido en la página para hacer filtrado de datos (ver capítulo correspondiente en este libro). Lo que hace es obtener una referencia al modelo de tabla apropiado basándose en la ruta actual de la página (ver epígrafe 8 de este capítulo), pero es exactamente lo mismo que el código anterior. De hecho podríamos haber usado el mismo aquí también en lugar de este. Los metadatos de la tabla nos ofrecen el método GetActionPath, que nos informa de la ruta apropiada para realizar las distintas acciones sobre la entidad. así, por ejemplo, para obtener la ruta que genera la página de inserción para la entidad actual podríamos usar:
table.GetActionPath(PageAction.Insert)

Lo mismo con el resto de acciones: edición (PageAction.Edit), detalles (Pageaction.Details) y listado de registros (PageAction.List). en asP.NeT 4.0 esto no es necesario porque el nuevo control DynamicHyperLink se encarga de esto automáticamente como veremos ahora mismo analicemos ahora el marcado hTML de página. si analizas los controles incluidos verás que, quitando un par de ellos, todos los demás son controles normales de asP.NeT, como una rejilla y un control DataSource. el primer control que nos encontramos es un DynamicDataManager. La funcionalidad de Dynamic Data se basa en el uso de unos cuantos controles especiales que habilitan el enlazado automático con el modelo de datos. como ya he comentado en la introducción, algunos controles como las rejillas se han modificado para soportar enlazado dinámico a datos y de esta manera generar listados y editar registros basándose en las plantillas. el control DynamicDataManager es un control no visual cuya función precisamente es habilitar este soporte dinámico en ciertos controles de la página. en concreto las tareas que realiza son: • Generar en los controles GridView o DetailsView las columnas necesarias para que se puedan mostrar o editar los campos de la tabla/entidad de datos.

AsP.net Dynamic Data: interfaces de datos a la velocidad de la luz 127

ajustar el tipo de contexto de datos y el nombre de la tabla en el control LinqDataSource o EntityDataSource para que puedan trabajar con la tabla de datos correcta. establecer los parámetros de filtrado (WhereParameters) en el control origen de datos para poder hacer filtros. cargar automáticamente las claves externas en el caso de modelos Linq.

• •

si miras un poco más abajo en el código verás una rejilla, GridView1, que será la encargada de listar los registros de datos, función principal de esta página. en este caso, como este es el control que utilizaremos con Dynamic Data, deberá ser activado para que soporte los controles dinámicos, por eso la plantilla tiene este marcado:
<asp:DynamicDataManager ID=”DynamicDataManager1” runat=”server” AutoLoadForeignKeys=”true”> <DataControls> <asp:DataControlReference ControlID=”GridView1” /> </DataControls> </asp:DynamicDataManager>

Lo que estamos indicando de manera declarativa es que el control DynamicDataManager debe habilitar el soporte para controles dinámicos en la rejilla GridView1. esto es lo que activa realmente toda la funcionalidad de la rejilla, pues a partir de este momento el control de origen de datos asociado a ésta (un LinqDataSource en nuestro ejemplo) ya sabrá qué datos debe manejar, y los controles dinámicos contenidos en la rejilla podrán mostrar la información. De hecho la rejilla está configurada para generar automáticamente las columnas a partir de todos los campos que haya en la entidad de datos que maneje. Los dos elementos definidos en su <itemTemplate> son simples enlaces para editar y ver los detalles del registro actual. el resto de los campos se genera al vuelo. Una cuestión importante a tener en cuenta si utilizas asP.NeT 3.5 sP1 es que, en esta primera versión de Dynamic Data, no estaba soportada la activación declarativa de los controles como acabamos de ver, y debía activarse mediante código al inicializar la página, así:
Protected Sub Page_Init(ByVal sender As Object, ByVal e As EventArgs) DynamicDataManager1.RegisterControl(GridView1, True) End Sub

Lo que sería equivalente al marcado anterior. Tenlo en cuenta si intentas reproducir estos ejemplos en una versión anterior a visual studio 2010. La paginación de la rejilla se efectúa con un control llamado <asp:GridViewPager>. No lo busques en el modelo de objetos de asP.NeT pues no es un control estándar a pesar de que lleva el prefijo asp (deberían haber elegido otro más claro). en realidad

128

tecnologías AsP.net 4.0 (saltando desde la versión 2.0)

es un control de usuario que encontrarás en la subcarpeta “DynamicData\content”. Por defecto incluye botones para avanzar por las páginas, un cuadro de texto para saltar a una página directamente, y una lista desplegable para elegir el número de elementos por página.

Figura 6.- barra de paginación de los listados automáticos

si editamos este control (GridViewPager.ascx) podremos personalizar completamente el aspecto y funcionalidad de la paginación de registros. Los gráficos utilizados están en esa misma carpeta dentro de “Images”. especial mención merece la forma en la que se generan los filtros automáticos para el listado. el control utilizado es un QueryableFilterRepeater. este control no lo encontrarás en la barra de herramientas de visual studio y es necesario añadirlo a mano a la página. su función es la de generar los controles que pueden usarse para filtrar los registros de la rejilla. Por defecto se generan controles en forma de lista desplegable para todos los campos que son una clave externa de la tabla y para los campos booleanos. este control repetidor se usa para filtrar datos en el DataSource correspondiente usando para ello un control QueryExtender (ver capítulo 7).
Nota:
en la primera versión de Dynamic Data, en asP.NeT 3.5 sP1, no existen estos controles, por lo que se utiliza en su lugar un control FilterRepeater. este control proporciona directamente al origen de datos una lista de parámetros Where que realizan el filtro, ya que no existe tampoco el control QueryExtender, disponible sólo a partir de asP.NeT 4.0.

en la figura 7 podemos ver los filtros automáticos que se han generado para la tabla de productos que son tres: uno para el único campo booleano (si el producto está o no descatalogado) y otros dos para las tablas relacionadas con la actual (categorías y proveedores).

Figura 7.- Filtros automáticos para tablas.

AsP.net Dynamic Data: interfaces de datos a la velocidad de la luz 129

el control desplegable se puede cambiar por cualquier otro o también es posible modificar su comportamiento partiendo del original. Para ello podemos modificar la plantilla apropiada dentro de la subcarpeta “Filters” que se ve en la figura 5 y que, por defecto, contiene una plantilla para los booleanos, otra para las enumeraciones y una tercera para las tablas relacionadas, que es la que se ha utilizado aquí.
Nota:
en la versión 3.5 sP1 no existe la carpeta “Filters” y hay, sin embargo, un control en la carpeta “content” que se llama “FilterUsercontrol.ascx” y tiene el mismo propósito

el resto de las páginas/plantillas son tremendamente parecidas, por lo que no te costará mucho comprender su funcionamiento y poder manipularlas a tu antojo. asegúrate de entenderlas bien porque, al hacerlo, tendrás casi todo lo que necesitas para crear tus propias páginas que saquen partido a Dynamic Data, pero sin tener que usar la generación automática de código. Luego volveremos sobre ello.

6.- PlANTillAS PArA ENTidAdES
Una de las cosas que probablemente te llamará la atención cuando analices las plantillas anteriores es que, en las páginas de detalles, la edición o la inserción de registros, se emplea un control llamado DynamicEntity. se trata de una nueva característica introducida en la versión 4.0 de la plataforma que permite tener mayor control sobre la forma de generar las interfaces de trabajo con elementos individuales. en la primera versión de Dynamic Data los registros individuales se mostraban tal cual estaban definidos en las plantillas “Details.aspx”, “edit.aspx” e “Insert. aspx”. Desde la versión 4.0 es posible hilar más fino y definir plantillas de edición especiales para cada tipo de entidad. Para ello en la carpeta “entityTemplates” hay tres plantillas para trabajar con entidades individuales: visualización (“Default.ascx”), edición (“Default_edit.ascx”) e inserción (“Default_Insert.ascx”) respectivamente. Éstas contienen el código de marcado que se usará para realizar cada una de estas acciones con todos los tipos de entidades. Podemos crear nuestras propias plantillas especiales para entidades concretas con tan solo crear controles análogos a estos con el nombre de la entidad/tabla. Por ejemplo, para ajustar a nuestra voluntad el trabajo con los registros de la tabla de productos podemos crear los controles: “Products.ascx”, “Products_edit.ascx” y “Products_Insert.ascx”. No es obligatorio crear los tres, sólo los que nos interesen. en tiempo de ejecución los controles DynamicEntity de las páginas plantilla se sustituyen por el control apropiado de la carpeta “entityTemplates”.

7.- PlANTillAS PArA cAMPoS
Mucho más interesantes que las plantillas de páginas son las plantillas para campos. cada campo de las tablas en la base de datos se corresponde con una propiedad en alguna de las entidades del modelo de datos. Para visualizar y editar los valores

130

tecnologías AsP.net 4.0 (saltando desde la versión 2.0)

de los campos, Dynamic Data utiliza para cada una el control que le parece más apropiado. así, por ejemplo, para campos de texto usará un TextBox para editar el campo, y un control Literal para visualizarlo. en el caso de los booleanos se usarán marcas de verificación (CheckBox), que estarán deshabilitadas en el caso de visualización. Dentro de la carpeta “DynamicData/FieldTemplates” están las plantillas utilizadas para los tipos de datos soportados por defecto.

Figura 8.- Plantillas de campos de datos dinámicos

son todos controles de usuario. Disponemos de un par de plantillas para cada tipo de campo: una para mostrarlo y otra para editarlo, siendo estas últimas las que llevan el sufijo _Edit. si abrimos cualquiera de ellas veremos que son controles de usuario normales, que llevan controles Web comunes dentro. Lo único un

AsP.net Dynamic Data: interfaces de datos a la velocidad de la luz 131

poco especial es el código de inicialización que llevan, ya que heredan de la clase FieldTemplateUserControl, y hacen uso de algunos de sus métodos para configurar los controles Web que contienen. examina con un poco de calma el código y verás que no tiene dificultad alguna. Por defecto disponemos de controles para campos booleanos, fecha y hora, números con decimales, números enteros, texto multi-línea, texto de una sola línea, direcciones de correo electrónico y UrLs (estos dos últimos sólo a partir de asP. NeT 4.0). Fíjate en que existen varias plantillas especiales: • Children.aspx: genera un enlace para ver los registros relacionados con el registro actual en una relación de tipo uno a muchos (maestro-detalle). Por ejemplo en una tabla de pedidos enlazaría con el listado de las líneas de pedido. Children_Insert.aspx: para facilitar la selección de una entidad hija al editar un registro. No está disponible en la primera versión de Dynamic data. Enumeration.ascx: si a uno de los campos de tipo entero del modelo le asignamos como tipo de datos una enumeración que tengamos definida en el código y que mapee los posibles valores para el campo, Dynamic Data usará esta plantilla. así, a la hora de visualizar el campo mostrará el nombre apropiado de la enumeración en lugar de un simple número. esta plantilla y la siguiente no están disponibles en asP.NeT 3.5 sP1, pero funcionarán si las creas a mano. Enumeration_Edit.ascx: idem que la anterior pero para facilitar la edición del campo eligiendo el valor desde una lista desplegable. ForeignKey.aspx: genera un enlace para ver los detalles de un registro relacionado de tipo uno a uno. Por ejemplo en el caso de un pedido para visualizar los detalles del cliente asociado con éste. ForeignKey_Edit.aspx: facilita la selección de un registro relacionado mediante una lista desplegable a la hora de editar el registro principal.

• •

• •

si cambiamos cualquiera de estas plantillas conseguiremos modificar su comportamiento y/o aspecto en todos los campos de la base de datos que hagan uso de ella. Por ejemplo, vamos a modificar la plantilla para edición de fechas de modo que nos ofrezca un calendario desplegable en lugar de tener que escribirlas a mano. abre el control “DateTime_edit.ascx” y ponlo en modo de diseño para que se vea su aspecto en el editor. selecciona el cuadro de texto, deshabilítalo poniendo su propiedad Enabled a False, y en su lista de tareas añade un control extensor (necesitarás tener instalado el AJAX Control Toolkit para que funcione). en la lista de extensores escoge el CalendarExtender. Graba el control y ejecuta de nuevo la aplicación.

132

tecnologías AsP.net 4.0 (saltando desde la versión 2.0)

ahora verás como cuando edites un registro que tenga fechas (por ejemplo un pedido de la tabla Orders) aparecerá un calendario desplegable para facilitar su selección. Juega un poco con estas plantillas para aprender bien a tomar control sobre ellas. Más adelante en este capítulo estudiaremos cómo podemos crear plantillas propias para ciertos campos de la base de datos o para tipos de datos que no son reconocidos por Dynamic Data.

8.- lAS ruTAS dE lAS PÁgiNAS diNÁMicAS
si te fijas bien en las UrL de las páginas autogeneradas por Dynamic Data, verás que se corresponden con rutas análogas a las siguientes:
http://localhost:xxxx/DynamicDataEjemplo/Products/List.aspx http://localhost:xxxx/DynamicDataEjemplo/Products/Edit. aspx?ProductID=1 http://localhost:xxxx/DynamicDataEjemplo/Customers/Insert.aspx

Puedes comprobar que ninguna de estas rutas existe en nuestra aplicación. es decir, apuntan a carpetas y archivos imaginarios. en realidad son rutas ficticias que la infraestructura de asP.NeT se encarga de redirigir a recursos reales en el servidor para procesarlas. en este caso las rutas ficticias apuntan a las plantillas asPX que hemos visto en los apartados anteriores. con esta técnica de enrutado conseguimos un doble objetivo: 1. Los usuarios obtienen rutas fáciles de recordar para efectuar operaciones con las entidades. 2. a través de la propia ruta se le pasa información de contexto a las plantillas para que sepan con qué entidad deben trabajar y qué acción van a efectuar, ya que en la propia UrL tiene el nombre de la entidad, la acción y, si procede, la clave primaria del registro. Las rutas se definen en Global.asax, en el mismo evento RegisterRoutes que utilizamos en el apartado 4 de este capítulo. si vuelves sobre el mismo verás que, por defecto, viene ya una ruta definida con el siguiente código:
routes.Add(New DynamicDataRoute(“{table}/{action}.aspx”) With { .Constraints = New RouteValueDictionary(New With {.Action = “List|Details|Edit|Insert”}), .Model = DefaultModel})

Lo que se hace aquí es añadir una nueva ruta ficticia para que asP.NeT la interprete y se la asigne a Dynamic Data para trabajar. el constructor de la ruta dinámica toma como parámetro una cadena en la que se marca con comodines la información que ésta contendrá. en este caso se indica que las rutas, siempre consideradas desde la raíz de la aplicación, tendrán la siguiente forma:
NombreTabla/Accion.asppx

AsP.net Dynamic Data: interfaces de datos a la velocidad de la luz 133

como podemos comprobar esto se corresponde con las rutas que he puesto de ejemplo en el párrafo anterior, que servían respectivamente para listar un producto, editarlo y para insertar un nuevo cliente (fíjate en cómo llevan el nombre de la entidad y la acción en la UrL). También junto con el constructor se ajustan algunas restricciones de ámbito para de la ruta a través de la clase RouteValueDictionary: • Action: define las acciones sobre entidades a las que responderá esta ruta. en este caso se han incluido todas las posibles, o sea, listar, ver detalles, editar e insertar. Model: el modelo de metadatos que se gestionará con esta ruta. Podemos tener más de uno. en nuestro ejemplo usamos el que habíamos definido al principio. Tables: no está presente en la línea de ejemplo, pero nos permite establecer qué tablas en concreto del modelo se van a gestionar con esta ruta. si no lo indicamos se usa para todas las tablas contenidas en el modelo. Luego veremos un ejemplo.

La ruta la podemos construir a nuestra voluntad. Por ejemplo, vamos a cambiar la ruta por defecto para que sea un poco más estilo resT (http://es.wikipedia.org/ wiki/REST). cambia la definición de la ruta por esta cadena:
{action}/{table}

Y deja lo demás igual. ejecuta la aplicación.

Figura 9.- Nuevas rutas estilo rEST para las páginas dinámicas

a partir de ahora verás como las rutas han cambiado por completo y que ni siquiera llevan una extensión .aspx ni apuntan a páginas ficticias concretas. son más

134

tecnologías AsP.net 4.0 (saltando desde la versión 2.0)

fáciles de recordar todavía para los usuarios pues éstos sólo tienen que saber qué acción quieren efectuar y sobre qué tabla. el manejo de rutas es una técnica muy potente y podemos hacer muchas otras cosas. recordarás que entre las plantillas existía una llamada “ListDetails.aspx” que en realidad no se estaba usando en ningún lado. cuando la mencioné dije que era una página “todo en uno” ya que en ella teníamos todo lo necesario para gestionar una entidad: listado, creación, edición y borrado. Pues vamos a usarla en lugar de las páginas individuales que hemos venido usando hasta ahora. convierte en un comentario la definición de la ruta que acabamos de hacer y descomenta la siguiente ruta que encontrarás ya preparada en el evento RegisterRoutes en el ejemplo descargable:
routes.Add(New DynamicDataRoute(“{table}/Mantenimiento”) With { _ .Action = PageAction.List, _ .ViewName = “ListDetails”, _ .Model = DefaultModel })

ejecuta la aplicación. verás que la página generada para cada entidad tiene todo lo necesario para gestionarla por completo, ya que desde la misma página -y estilo aJaX- podemos editar, crear, borrar y ver el detalle de cualquier registro de cualquier tabla de nuestro modelo. Y lo mejor de todo es que para que un usuario acceda al mantenimiento de cualquier tabla lo único que tiene que hacer es poner el nombre de la misma seguido de la palabra “Mantenimiento”, por ejemplo:
http://localhost/DynamicDataEjemplo/Suppliers/Mantenimiento

¡No está nada mal! ¿verdad? en este caso hemos indicado sólo el comando pageAction.List ya que con la plantilla que hemos seleccionado todos los comandos se gestionan desde esa página. Por eso tampoco tenemos la acción especificada en la ruta. Las rutas se pueden utilizar para definir nuestras propias plantillas de trabajo para las entidades. Por ejemplo, imagina que creamos una plantilla propia que hace la edición de las entidades de algún modo especial y queremos usarla en lugar de las estándar. si a la plantilla le hemos llamado “MisDetalles.aspx” y la tenemos en la carpeta “DynamicData”, junto con la demás, podemos definir la ruta de esta manera:
routes.Add(New DynamicDataRoute(“{table}/Mantenimiento”) With { _ .Action = PageAction.List, _ .ViewName = “MisDetalles”, _ .Model = model})

es decir, sólo cambiamos el modelo de la vista y ya funcionaría. en el proyecto demo descargable se ha incluido este ejemplo también.

AsP.net Dynamic Data: interfaces de datos a la velocidad de la luz 135

es posible crear plantillas específicas para tablas concretas. Para eso está la carpeta “DynamicData/customPages”. Para crear plantillas especiales para una entidad debemos crear una subcarpeta dentro de “customPages” con el nombre de la tabla en la base de datos e incluir dentro las plantillas específicas que necesitemos. serían plantillas normales, como las que hay en la raíz, pero se usarían sólo para esa entidad. si falta alguna acción se seguiría usando la plantilla por defecto. en el proyecto de ejemplo descargable de la Web he personalizado las plantillas que se usarán con la tabla de productos, que están en la carpeta “DynamicData/ customPages/Products”. De esta forma al entrar en cualquier acción de los productos se usarán estas plantillas en lugar de las predeterminadas.

8.1.- Parámetros individuales en las rutas
en las rutas personalizadas que se refieren a un único registro, por ejemplo las rutas de edición, éste se le pasa como un parámetro a la página de forma análoga a esta:
http://localhost:xxxx/DynamicDataEjemplo/Products/Edit. aspx?ProductID=1

Podemos conseguir una UrL con cualquier otra forma usando el nombre de la clave primaria como comodín en la ruta personalizada. en este caso hay que especificar la tabla concreta que queremos utilizar, ya que cada entidad maneja claves primarias diferentes. Por ejemplo, podemos conseguir que los productos se editen con una ruta estilo resT, especificando el ID al final de la misma, usando esta ruta:
routes.Add(New DynamicDataRoute(“Products/Mantenimiento/{ProductID}”) With { _ .Action = PageAction.Edit, _ .ViewName = “Edit”, _ .Table = “Products”}) .Model = DefaultModel, _

que nos daría rutas como la siguiente, mucho más claras:
http://localhost:xxxx/DynamicDataEjemplo/Products/Mantenimiento/1

Esta ruta abriría directamente el producto con ID = 1 en modo de edición. Fíjate en que en esta ruta hemos empleado una propiedad adicional llamada Table, que ata la ruta a una entidad concreta. como comentario final sobre las rutas he de añadir que es muy importante el orden en el que las definamos dentro de Global.asax. Las rutas se evaluarán secuencialmente en el orden en que fueron definidas. en cuanto se encuentre una que coincida se parará el procesamiento del resto de rutas. Por ello hay que ser muy cuidadosos cuando las agreguemos para evitar que unas “tapen” a las otras y no consigamos el efecto buscado.

136

tecnologías AsP.net 4.0 (saltando desde la versión 2.0)

9.- AMPliANdo loS METAdAToS dEl ModElo
sin que tengamos que hacer nada especial, Dynamic Data ya se ocupa de efectuar multitud de validaciones por nosotros: que se introduzcan valores del tipo apropiado, que los campos que no admiten nulos sean obligatorios, etc... Toda esta información la obtiene directamente del modelo de datos, ya que tanto Linq2SQL como Entity Framework incluyen metadatos para indicar este tipo de restricciones. sin embargo raramente estas restricciones por defecto van a ser suficientes. hay una serie de restricciones que no se pueden extraer de la base de datos y por lo tanto del modelo. se trata de reglas de negocio que se deducen de los requerimientos específicos de nuestra aplicación. Los hay generales (por ejemplo, un producto no puede tener stock negativo) y particulares de nuestro desarrollo (nuestros requisitos del proyecto nos indican que no podemos descatalogar un producto que todavía tenga stock). estas reglas de negocio las podemos incluir en el modelo de datos para forzar su cumplimiento a la hora de introducir o editar registros. existe un espacio de nombres llamado System.ComponentModel.Annotations, que ofrece una serie de atributos especiales destinados a proporcionar información extra a DynamicData acerca del modelo de datos.

Figura 10.- El espacio de nombre Annotations y sus atributos.

Gracias a estos atributos podremos indicar a Dynamic Data mucha información extra útil para generar la interfaz. ahora mismo veremos la manera de hacerlo. si abres el archivo Northwind.designer.vb de nuestro modelo de datos Linq2sQL verás el código autogenerado para describir las tablas y efectuar todas las operaciones con la base de datos. Busca la definición de alguna de las entidades, por ejemplo Product, y comprobarás que se trata de una clase parcial.

AsP.net Dynamic Data: interfaces de datos a la velocidad de la luz 137

Nota:
Las clases parciales son una interesante característica de los lenguajes .NeT que permite definir una clase separando el código de la misma en dos o más archivos diferentes. Estas clases están pensadas para que varios programadores puedan implementar partes de la clase de forma independiente unos de otros y, sobre todo, para poder complementar el código generado por herramientas automáticas. este es el caso que nos ocupa ahora. así, podemos ampliar las capacidades de nuestras clases que representan entidades usando un archivo aparte. De este modo si debemos regenerar el modelo porque ha habido algún cambio, las modificaciones y añadidos hechos en el archivo independiente no se verán afectados, cosa que sí ocurriría si tocamos directamente el código original.

vamos a ampliar esa clase parcial para darle información adicional a la que trae el modelo. crea una nueva clase con el nombre Product dentro de la carpeta App_Code. asegúrate de ponerle la palabra clave Partial en su definición. esta representará a la misma entidad Product que hay en nuestro modelo. añade los siguientes espacios de nombres en la parte de arriba del archivo:
Imports System.Data.Linq Imports System.Web.DynamicData Imports System.ComponentModel.DataAnnotations

(en c# sería con la palabra clave using). Por desgracia no podemos utilizar los atributos directamente sobre esta clase parcial que hemos creado, ya que las propiedades que representan a los campos de la base de datos no pueden redefinirse (ya están definidas en el código autogenerado y no son “propiedades parciales”). Por ello tendremos que hacerlo indirectamente. el modo de conseguirlo es crear otra clase “de paja” que realmente sólo servirá para aplicar atributos a campos y ofrecer información al modelo. enseguida lo entenderemos. vamos a decorar la clase con un atributo especial: MetaDataType. Éste sirve para indicar el nombre de esa clase auxiliar sobre la que aplicaremos los atributos informativos. La definición de nuestra clase parcial para la entidad quedaría así:
<MetadataType(GetType(MetaDataDeProducto))> _ Partial Public Class Product End Class

La clase MetaDataDeProducto todavía no existe, pero la vamos a crear justo a continuación. el nombre que le demos es indiferente, sólo tiene que coincidir con el que hayamos indicado en el atributo MetaDataType. en ella crearemos campos con el mismo nombre que los campos de la tabla del modelo. Éstos nos servirán simplemente como medio para aplicar atributos diversos y proporcionar información a Dynamic Data. Pensemos en esta nueva clase como una clase “tonta” que sólo sirve para mapear campos del modelo con características de éste. Por ejemplo,

138

tecnologías AsP.net 4.0 (saltando desde la versión 2.0)

vamos a definir dos campos de la tabla de productos para que sean excluidos de la generación automática:
Class MetaDataDeProducto <ScaffoldColumn(False)> _ Public ReorderLevel As Object <ScaffoldColumn(False)> _ Public UnitsOnOrder As Object End Class

Fíjate en que simplemente hemos creado dos miembros con el mismo nombre que tienen sendos campos de la tabla productos. el tipo que se indique para éstos va a ser indiferente, puesto que Dynamic Data sólo se fijará en su nombre y en los atributos que los decoren, por eso hemos puesto Object. a cada uno de estos miembros le podemos asignar tantos atributos del espacio de nombres Annotations (figura 10) como sean necesarios. en el listado anterior lo que estamos indicando con el atributo ScaffoldColumn es que Dynamic Data no debe usar estos dos campos de datos cuando genere las interfaces, ni para visualizar ni para editarlos. ejecuta la aplicación y vete a la tabla de productos verás que ambos campos han desparecido.

9.1.- Mejorando la validación de campos
existen tres atributos de validación que podemos utilizar y que nos ayudarán a incluir condiciones adicionales a las que tiene el propio modelo de datos: • RangeAttribute: especifica un rango de valores posibles para un campo. es posible indicar también el mensaje a mostrar si el valor indicado excede el intervalo. RegularExpressionAttribute: indicaremos una expresión regular que deberá cumplir el texto que se asigne al campo. Muy útil para indicar expresiones comunes, como la sintaxis de un email o el formato de un teléfono. RequiredAttribute: para forzar a que un campo sea obligatorio. Normalmente no se usa mucho porque los campos obligatorios suelen estar marcados como no nulos en la base de datos, pero puede venir bien si no lo hemos marcado por algún motivo (por ejemplo porque tratamos los valores nulos de forma especial en el programa pero no dejamos que los usuarios los puedan introducir). DataTypeAttribute: para indicar el tipo específico del dato de entre los disponibles en la enumeración DataType. Podemos ver sus valores a la derecha

otros atributos, sin ser de validación, que podemos usar son: •

AsP.net Dynamic Data: interfaces de datos a la velocidad de la luz 139

de la figura 10. así podemos indicar que un campo no es simplemente de texto (que es lo que se sacaría del modelo de datos) sino que es además específicamente una clave de acceso o va a guardar UrLs, con lo que la interfaz se podría especializar para responder a estos requisitos. Por ejemplo, si indicamos que es un email se usará la plantilla correspondiente que hemos visto en el apartado 7 de este capítulo. • • StringLengthAttribute: para indicar la longitud máxima de una cadena. DisplayColumnAttribute: sirve para especificar qué campo debemos usar para mostrar los detalles de una entidad asociada con la nuestra mediante una clave externa. Por ejemplo el nombre del proveedor en lugar de su ID cuando visualicemos un campo IDProveedor en una tabla relacionada. DisplayFormatAttribute: formato para usar al visualizar el dato. UIHintAttribute: nos permite especificar la plantilla a utilizar para visualizar y editar el campo actual. Nos sirve para editar algunos campos de modo especial o, directamente, para definir cómo se deben editar cuando son de tipo desconocido. Más sobre esto luego.

• •

así, por ejemplo, si queremos indicar que el precio de un producto no puede ser nunca menor que cero, podemos añadir este campo a la clase auxiliar:
<Range(0, Integer.MaxValue, ErrorMessage:=”El precio no puede ser menor que cero.”)> _ Public UnitPrice As Object

con lo que se validará automáticamente esta condición desde todos los lugares de la interfaz de usuario que usen Dynamic Data. La clase auxiliar para pedidos la podemos definir del siguiente modo:
Public Class MetaDataDePedidos <DataType(DataType.Date)> _ Public OrderDate As Object <DataType(DataType.Date)> _ Public RequiredDate As Object <DataType(DataType.Date)> _ Public ShippedDate As Object <DisplayFormat(DataFormatString:=”{0:F} Kg”, NullDisplayText:=””)> _ Public Freight As Object End Class

140

tecnologías AsP.net 4.0 (saltando desde la versión 2.0)

con lo que conseguiremos que las fechas de los pedidos se muestren exclusivamente como fechas (sin la hora, que también está en la base de datos), y que el peso del pedido se visualice en kilogramos, usando una cadena vacía en caso de que alguno contenga un nulo.

9.2.- validaciones personalizadas
adicionalmente todas las excepciones que generemos desde el modelo de datos serán capturadas y gestionadas por el validador dinámico en la interfaz de usuario. esto nos permite añadirle validaciones mucho más complejas a las entidades escribiendo un poco de código. el lugar apropiado para realizar la validación es en la clase parcial que hemos creado antes. si abrimos el código del modelo de datos (la clase parcial xxxx.designer.vb de la que hablamos antes), veremos que todas las entidades tienen una sección (“Definición de métodos de extensibilidad”) que contiene multitud de métodos parciales con nombres típicos de eventos (figura 11).

Figura 11.- Métodos de extensibilidad de las entidades

Todos ellos son métodos parciales. esto quiere decir que si no los implementamos en otra parte de la clase, el compilador los elimina ya que no se van a usar jamás. Pero si los implementamos se podrán utilizar durante la ejecución del programa. si nos fijamos bien veremos que hay dos métodos especiales (OnValidate y OnCreate para la validación global de la clase y la creación de la misma) y que el resto de métodos se corresponden con el nombre de los campos de la entidad y hay dos por cada campo. Los que se llaman OnXXXXChanging son métodos que

AsP.net Dynamic Data: interfaces de datos a la velocidad de la luz 141

se llamarán automáticamente justo antes de cambiar el valor de la propiedad, y los OnXXXXChanged se llamarán tras haber cambiado el valor. sabiendo esto añadir código de validación especial para un campo es una tarea sencilla, pues basta con implementar el método parcial correspondiente al cambio de valor de éste. Por ejemplo, para impedir mediante código que los stocks puedan ser negativos escribiríamos esto:
<MetadataType(GetType(MetaDataDeProducto))> _ Partial Public Class Product Private Sub OnUnitsInStockChanging(ByVal value As System.Nullable(Of Short)) If value < 0 Then Throw New ArgumentOutOfRangeException(“Los stocks no pueden ser negativos”) End If End Sub End Class

a partir de ahora se mostrará el mensaje indicado cuando un usuario intente introducir un valor de inventario negativo en algún producto. estos eventos no nos permiten validar unos campos en relación con otros de la misma entidad porque puede que no estén todavía establecidos cuando se ejecuta el evento (se van estableciendo por orden). si queremos conseguir una validación de este tipo -en la que entren en juego varios campos- deberemos usar el método parcial para la validación general de la clase, OnValidate:
Private Sub OnValidate(ByVal action As System.Data.Linq.ChangeAction) If action = ChangeAction.Insert Or action = ChangeAction.Update Then If Me.Discontinued And Me.UnitsInStock > 0 Then Throw New ArgumentOutOfRangeException(“No se puede asignar stock distinto de cero a productos descatalogados.”) End If End If End Sub

con este código comprobamos si estamos en una inserción o actualización, y en ese caso nos aseguramos de que si un producto está descatalogado no pueda tener productos en el almacén. Todas estas excepciones se convertirán en validaciones en la interfaz del usuario, por lo que disponemos de un mecanismo muy potente para crear reglas de negocio directamente en el modelo de datos. Éstas se cumplirán en todo momento independientemente de donde se usen posteriormente las entidades, sea con Dynamic Data o no.

10.- PlANTillAS dE cAMPoS ProPiAS
como hemos visto, dentro de la carpeta “DynamicData/FieldTemplates” están definidos los controles para visualizar y editar los tipos de datos más comunes. No obstante en muchos casos estas plantillas de controles serán insuficientes y necesitaremos

142

tecnologías AsP.net 4.0 (saltando desde la versión 2.0)

editar los datos de alguna forma especial o bien habrá algún tipo de dato que no sea reconocido. en estos casos podemos crear nuestras propias plantillas de campos. Lo primero que tenemos que decidir es el nombre de la plantilla, ya que será la forma que tenga Dynamic Data de reconocerla y usarla. como ya hemos visto, pueden existir dos variantes de una plantilla de campo: para visualización o para edición. ambas llevan el mismo nombre sólo que la de edición va seguida del sufijo _Edit. supongamos que queremos crear una plantilla que nos permita editar algunos campos que pueden contener hTML. en lugar de usar el control de texto normal podemos usar un control editor de hTML. en el código de ejemplo descargable he creado un campo personalizado llamado HTMLText que permite visualizar y editar campos hTML. Para ello tengo dos archivos: “hTMLtext.ascx”, que sirve para visualizar, y “hTMLText_edit.ascx” que sirve para editar el contenido. ambos están en la carpeta “FieldTemplates” del proyecto. el control de visualización es un control Literal que mostrará cualquier hTML que le indiquemos en su propiedad Text. La única cuestión a tener en cuenta es que usaremos la propiedad FieldValue en lugar de la más habitual FieldValueString del control FieldTemplateUserControl del que hereda. el motivo es que la segunda devuelve la cadena formateada para hTML, por lo que visualizaríamos el código pero no se renderizaría en la página como hTML. el control de edición utiliza un control Editor de los que vienen disponibles gratuitamente en las versiones del aJaX control Toolkit a partir de la 3.0.30515 (descargable desde http://ajaxcontroltoolkit.codeplex.com/). el código es muy sencillo y casi idéntico al de cualquier otra plantilla: simplemente se ajustan los validadores y se define el contenido del control (propiedad Content de éste) como valor a extraer de la edición. Una vez creada la plantilla sólo nos resta indicar en los metadatos qué campos queremos que sean editables con ella. La forma de hacerlo es mediante el atributo UIHint, estudiado en el epígrafe 9.1. Por ejemplo, vamos a hacer que la descripción de las categorías de productos sean editables en formato hTML. el código necesario es el de la figura 12.

Figura 12.- código para asignar editor de campos personalizado

AsP.net Dynamic Data: interfaces de datos a la velocidad de la luz 143

el atributo indica el nombre del control y Dynamic Data se encarga de llamar a la versión apropiada según se esté visualizando o editando el campo. si sólo tuviésemos uno de las dos variantes se usaría el control más apropiado para el tipo de datos subyacente. así, en este ejemplo si no tuviésemos el control de visualización (o sea, “hTMLText.ascx”) se usaría el correspondiente a campos de texto: “Text.ascx”, y lo mismo en caso contrario. en la figura 13 vemos el editor de campos en funcionamiento, tanto en edición como en visualización.

Figura 13.- Nuestros controles para editar y visualizar hTMl

11.- dyNAMic dATA EN PÁgiNAS ProPiAS
Desde el principio del capítulo he comentado que es posible sacarle partido a esta tecnología sin usar necesariamente la parte de generación automática de interfaces ni las rutas. De hecho, salvo en los casos en los que hayamos protegido debidamente y de manera consciente el acceso a las rutas autogeneradas, es incluso mejor usar Dynamic Data sin la generación automática de interfaces.

144

tecnologías AsP.net 4.0 (saltando desde la versión 2.0)

en realidad a estas alturas ya sabes lo necesario para sacarle partido, pues todo lo que hemos visto es válido. si tenemos el modelo de datos correctamente registrado (del modo que se explica en el epígrafe 4 de este capítulo), lo único que tenemos que hacer en nuestras páginas para sacar partido a Dynamic Data es arrastrar un control DynamicDataManager a la superficie de diseño de la página.

Figura 14.- los controles de datos dinámicos

como ya hemos visto en el punto 5.1, este control registra a otros controles para poder sacarle partido a la generación automática de campos de Dynamic Data. si en nuestra página tenemos un GridView y un control LinqDataSource configurados para trabajar conjuntamente, sólo tenemos que registrar la rejilla con nuestro DynamicDataManager para que ésta sea capaz de generar la visualización y edición de las columnas de datos, bien declarativamente:
<asp:DynamicDataManager ID=”DynamicDataManager1” runat=”server” AutoLoadForeignKeys=”true”> <DataControls> <asp:DataControlReference ControlID=”GridView1” /> </DataControls> </asp:DynamicDataManager>

AsP.net Dynamic Data: interfaces de datos a la velocidad de la luz 145

Bien mediante código
Protected Sub Page_Init(ByVal sender As Object, ByVal e As System. EventArgs) Handles Me.Init DynamicDataManager1.RegisterControl(GridView1) End Sub

ahora, en lugar de usar los campos enlazados normales de una rejilla podemos utilizar los campos dinámicos simplemente indicando el nombre del campo que van a representar:
<asp:GridView ID=”GridView1” runat=”server” AllowPaging=”True” AllowSorting=”True” AutoGenerateColumns=”False” CellPadding=”4” DataKeyNames=”CategoryID” DataSourceID=”LinqDataSource1” GridLines=”None” PageSize=”3”> <Columns> <asp:DynamicField DataField=”CategoryName” runat=”server” /> <asp:DynamicField DataField=”Description” runat=”server” /> </Columns> </asp:GridView>

al usar este tipo especial de campos enlazados, soportados nativamente por los controles GridView y DetailsView, podremos sacar partido a todo lo que hemos visto sobre Dynamic Data. se usarán las plantillas de controles apropiadas para cada uno, se hará la validación automática de los campos, etc... ello nos libera de una gran cantidad de trabajo pues no tendremos que preocuparnos por editar cada una de las columnas de la rejilla para hacer validaciones, dar formatos, permitir la selección de elementos relacionados, etc... Los controles ListView y FormView permiten también todo el trabajo con campos dinámicos, pero en este caso no soportan los campos enlazados de tipo DynamicField. Para poder exprimir la potencia de los datos dinámicos en este caso, debemos incluir controles de tipo DynamicControl dentro de su marcado. Básicamente estos controles dinámicos se comportan igual que los campos dinámicos de la rejilla, pero los podemos usar en el hTML de estos otros controles que no tienen soporte nativo para datos dinámicos. Por ejemplo, un ListView podría tener la siguiente definición para sus Items:
<ItemTemplate> <tr> <td>

<asp:DynamicControl runat=”server” DataField=”ProductID” /> </td> <td> <asp:DynamicControl runat=”server”

146

tecnologías AsP.net 4.0 (saltando desde la versión 2.0)

DataField=”ProductName” /> </td> <td> <asp:DynamicControl runat=”server” DataField=”Supplier” /> </td> <td> <asp:DynamicControl runat=”server” DataField=”UnitPrice” /> </td> <td> <asp:DynamicControl runat=”server” DataField=”Discontinued” /> </td> </tr> </ItemTemplate>

como vemos, tan fáciles de utilizar como los campos dinámicos enlazados de la rejilla. Del mismo modo podría emplear un control DinamycEntity como hemos visto al analizar las plantillas. Todo lo que hemos visto de modificar los metadatos del modelo, asignar controles específicos para edición y visualización, etc... sigue siendo válido para estos controles y los utilizaremos igual. La mejor forma de aprender a usarlos en la práctica es analizar bien, como ya dije anteriormente, las plantillas por defecto que trae visual studio para la interfaz autogenerada, pues en realidad lo único que hacen éstas es deducir de la ruta qué tablas van a utilizar. el resto es lo mismo que podríamos hacer nosotros en cualquier página.

12.- EN rESuMEN
Dynamic Data es una tecnología espectacular aparecida con el service pack 1 de .NeT 3.5 y disponible en ediciones superiores, que nos permite generar interfaces completas de gestión de datos de manera automática. Podemos personalizar casi sin límite el comportamiento de las interfaces generadas. cualquier programador Web podrá ver la ganancia de productividad que obtiene con esta tecnología sin perder por ello potencia ni capacidad de adaptación a necesidades concretas.

cAPíTulo

7

Filtrado de datos automático con Queryextender
Una de las tareas más comunes que debemos realizar como programadores es la de filtrar datos. Imagínate el típico listado en una aplicación, por ejemplo para mostrar todos los productos de la base de datos “Northwind”. como son muchos, debes facilitar a los usuarios alguna manera de hacer filtros sobre los datos visualizados. De esta manera localizarán fácilmente aquellos en los que estén realmente interesados. Lo habitual en estos casos es que tengas que interceptar el evento de algún botón de búsqueda, y entonces modificar dinámicamente la cláusula Where de la consulta. otra opción bastante utilizada es gestionar el evento de tipo Selecting del control origen de datos, en el cual podrás cambiar también la consulta dinámicamente. si bien estas técnicas son relativamente sencillas, nos obligan a escribir código y son difíciles de mantener. cada vez que queramos modificar la forma de hacer la búsqueda o si necesitamos añadir más filtros a la misma tendremos que tocar el código y recompilar. en asP.NeT 4.0 se incluye un nuevo control -y clases relacionadas- que nos permiten crear filtros de manera declarativa, sin necesidad de escribir código alguno. es otra gran idea para mejorar la productividad en tareas comunes.

1.- El coNTrol QuEryEXTENdEr
el nuevo control QueryExtender se utiliza para definir de forma declarativa filtros sobre orígenes de datos, pudiendo incluir tantas condiciones como sea necesario, y sin necesidad de escribir código. como resultado se obtiene un conjunto de resultados que cumpla las condiciones especificadas, ordenado según el criterio que hayamos decidido. Los QueryExtender funcionan exclusivamente con controles de origen de datos para Linq2sQL (LinqDataSource) y para entity Framework (EntityDataSource), ya que el lenguaje de consulta que manejan por debajo es Linq. otra cosa interesante a tener en cuenta al usarlos es que modifican directamente la expresión Linq manejada por el control origen de datos, por lo que en realidad de la base de datos se obtienen ya los datos filtrados. es decir, no es un control
147

148

tecnologías AsP.net 4.0 (saltando desde la versión 2.0)

que actúe a posteriori sobre los datos recibidos, procesándolos en memoria, sino que trae únicamente los datos necesarios para mostrar en cada caso, por lo que el rendimiento es muy bueno.

2.- TiPoS dE FilTroS
como veremos enseguida, el control QueryExtender es sólo un contenedor de expresiones de filtrado. Éstas son las que definen el filtro y las que asociamos a otros controles de la interfaz para que tomen de ellos sus valores (parametrización del filtro). Los filtros se definen, por tanto, como una jerarquía de controles cuyo marcado debemos incluir en la página. en la raíz de la misma está el control Queryextender. como hijos de éste se incluyen uno o varios controles que representan tipos de expresiones de filtrado. a su vez estas expresiones contienen parámetros para particularizar los valores de filtrado:
QueryExtender Filtro1

Filtro2 .....

Parámetro1 Parámetro2 Parámetro1

Disponemos de las siguientes expresiones de filtrado que podemos declarar dentro del control: • SearchExpression: busca en la base de datos una cadena de texto. Puede buscar en uno o varios campos a la vez, y podemos especificar en dónde queremos que esté la cadena objetivo de la búsqueda: al principio del campo, al final o en cualquier punto del campo (sería equivalente a una búsqueda sQL con LIKe usando ‘%cadena’, ‘cadena%’ y ‘%cadena%’ respectivamente). RangeExpression: hace el filtro de un campo comparando sus valores con unos valores mínimo y máximo especificados en un par de controles. PropertyExpression: compara el valor de la propiedad de un control con los valores de un campo en el origen de datos. Por ejemplo, puedes usar el valor seleccionado en una lista desplegable para mostrar los registros que tengan un campo que coincida con ese valor. Luego lo veremos con un ejemplo. CustomExpression: permite crear criterios de búsqueda personalizados. se le indica el nombre de un método propio que modifique mediante código la consulta Linq a lanzar contra el origen de datos. es tal vez el menos útil,

• •

Filtrado de datos automático con Queryextender 149

en mi opinión, puesto que es casi equivalente a los métodos tradicionales comentados al principio del capítulo y hace que pierda la gracia el sistema, ya que hay que escribir código. No obstante, en ocasiones puede ser útil. • MethodExpression: permite llamar a un método estático/compartido (indicado con el atributo MethodName) de una determinada clase (atributo TypeName) para realizar el filtrado dinámicamente. el método puede carecer de parámetros o, en caso de tenerlos, el primero deberá ser una referencia al control origen de datos, y los siguientes se corresponderán con cada uno de los nodos hijo de tipo XXXParameter que particularizan la expresión de filtrado. DynamicFilterExpression: permite hacer filtros a partir de controles DynamicFilter de Dynamic Data. Las has visto en el código de las plantillas del capítulo anterior. ControlFilterExpression: se utiliza en páginas con Dynamic Data para filtrar los valores de un control de datos en función del valor seleccionado en una rejilla u otro control enlazado. OrderByExpression: no es una expresión de filtrado, sino de ordenación. Nos permite devolver los resultados ordenados a partir de un campo (atributo DataField) y según una dirección (atributo Direction, con valores Ascending o Descending). es posible incluir varias de estas expresiones para ordenar según diversos criterios.

Las más utilizadas, y con las que trabajaremos habitualmente, son las tres primeras y la última de ordenación. Todas estas expresiones podemos utilizarlas de manera individual o combinarlas para obtener filtros complejos. cada uno de ellos se asocia a uno o varios controles para obtener los valores con los que trabajar. Ya es suficiente de teoría. Para aprender a manejar bien todo esto, vamos a crear primero una página sencilla de la que partir para añadirle, a continuación, funcionalidad de filtrado de datos con un control QueryExtender.

3.- crEAcióN dE lA PÁgiNA bASE PArA EJEMPlo
vamos a crear una página que nos servirá de base para construir diversos filtros con el control QueryExtender y así aprender a manejarlo. en el ZIP con las demos del libro se ha incluido una página por cada ejemplo, pero todas ellas usan la página que vamos a crear ahora como base. en un nuevo sitio Web agrega una nueva clase Linq2sQL llamada NorthWind. dbml. Di que sí cuando visual studio te pregunte si quieres crear la carpeta app_ code, pues es necesario que vaya dentro de ésta para poder utilizarlo.

150

tecnologías AsP.net 4.0 (saltando desde la versión 2.0)

Figura 1.- Agregar una nueva clase linq2SQl

Desde el explorador de servidores abre la base de datos “Northwind”. sobre la superficie de diseño del .dbml arrastra las tablas de proveedores (Suppliers) y de productos (Products). Te debería quedar algo con el aspecto mostrado en la figura 2.

Figura 2.- Nuestra entidad linq2SQl para trabajar.

Filtrado de datos automático con Queryextender 151

Graba esta entidad y a continuación, en una nueva página asPX, añade un ScriptManager y un UpdatePanel. Dentro del panel arrastra un control LinqDataSource. Desde la lista de tareas de éste escoge la opción de configurarlo. eso abrirá el asistente de configuración del origen de datos. en el primer paso pulsa simplemente siguiente ya que el contexto que está seleccionado es el que queremos usar (y el único disponible). en el segundo paso elige la tabla de Productos y marca los campos indicados en la figura 3.

Figura 3.- campos seleccionados en el contexto de datos

Tras aceptar el diálogo arrastra ahora un control Gridview y asígnale como origen de datos el anterior control, usando para ello las tareas asociadas a la rejilla. Marca las opciones de paginación (3 por página) y ordenación, y ajusta las cabeceras de las columnas como creas conveniente. en los archivos descargables desde la Web yo he añadido además una columna de tipo plantilla para mostrar el nombre del proveedor de cada producto. Último paso muy importante: para que te funcionen los distintos tipos de expresiones de filtro (y que visual studio te ofrezca soporte Intellisense para utilizarlos), deberás agregar una línea especial al archivo de configuración de tu sitio web, ya que de manera predeterminada no están registrados para su uso. Localiza el nodo <pages> dentro de web.config y desplázate hacia abajo hasta que encuentres el sub-nodo <controls>. al final de la lista añade la siguiente línea:
<add tagPrefix=”asp” namespace=”System.Web.UI.WebControls.Expressions” assembly=”System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35”/>

152

tecnologías AsP.net 4.0 (saltando desde la versión 2.0)

en cuanto grabes el web.config, las expresiones de filtro descritas en el epígrafe 2 de este capítulo estarán disponibles para su uso en tu aplicación. No te olvides de hacer esto último o te encontrarás con errores a la hora de compilar. ahora ejecuta la página para ver que todo funciona como es debido y que se visualiza un listado con todos los productos de la base de datos. Posteriormente podrás copiar y pegar todo lo que hay entre las etiquetas <html></html> de esta página asPX, para replicar su funcionalidad en otras nuevas que crees para hacer pruebas.

4.- PriMEr FilTro: búSQuEdA Por NoMbrE
Nuestro primer filtro será la típica casilla de búsqueda en la que introducimos un texto y se nos devuelven todos los productos que lo contengan en su nombre. en este caso además incluiremos también en la búsqueda el nombre de los proveedores (Suppliers), haciéndola sobre dos campos de la base de datos a la vez. arrastra un control de texto en la parte de arriba del panel y coloca justo delante un botón. este botón no tendrá código, y lo usaremos únicamente para provocar un Postback al servidor (asíncrono, porque está dentro de un UpdatePanel). Pasa a la vista de código hTML de la página y justo debajo de las etiquetas que definen el control LinqDataSource escribe la definición de un control QueryExtender, así:
<asp:QueryExtender ID=”QueryExtender1” runat=”server” TargetControlID=”LinqDataSource1”> </asp:QueryExtender>

el propio editor de visual studio te ayudará a escribir la etiqueta. en este punto lo único que hemos hecho es crear este control y usar su propiedad TargetControlID para asignarlo al origen de datos con el que trabaja la rejilla (LinqDataSource1). De esta forma todas las expresiones de filtrado que definamos dentro de él actuarán sobre este DataSource. entre las dos etiquetas del control QueryExtender puedes añadir tantas expresiones de filtrado como sean precisas. visual studio te ayudará con la sintaxis, como se observa en la figura 4.

Figura 4.- intellisense para las expresiones de filtrado

Filtrado de datos automático con Queryextender 153

Para nuestro ejemplo definiremos una expresión de tipo SearchExpression. el código declarativo quedaría de la siguiente manera (míralo en la página “search. aspx” del ZIP con los ejemplos del libro):
<asp:QueryExtender ID=”QueryExtender1” runat=”server” TargetControlID=”LinqDataSource1”> <asp:SearchExpression DataFields=”ProductName, Supplier.CompanyName” SearchType=”Contains” > <asp:ControlParameter ControlID=”TextBox1” /> </asp:SearchExpression> </asp:QueryExtender>

en la expresión de búsqueda indicamos sobre qué campos del origen de datos queremos buscar usando el atributo DataFields. además debemos decir si queremos que el texto buscado esté al principio (StartsWith), al final (EndsWith) o en cualquier posición del campo (Contains), que es lo que hemos escogido nosotros. como nodo hijo de la expresión se define un parámetro que indica de dónde queremos obtener el texto para realizar la búsqueda. en este caso es un parámetro que adquiere la cadena desde un control de la página, el cuadro de texto TextBox1, pero existen otras muchas posibilidades como veremos enseguida. como ves se trata de una jerarquía de etiquetas, tal y como se describe en el apartado 2. ejecuta la página. escribe parte del nombre de un producto o de un proveedor en el cuadro de texto y pulsa eNTer o presiona el botón. verás como automáticamente el contenido de la rejilla cambia y muestra sólo los productos cuyo nombre contiene ese texto. ¡Funciona! y todo sin escribir código de ningún tipo.

Figura 5.- Nuestro filtro en plena acción: ¡Mira mamá, sin código! ;-)

Incluir condiciones adicionales en el filtro es tan fácil como agregar nuevas expresiones dentro del control QueryExtender.

154

tecnologías AsP.net 4.0 (saltando desde la versión 2.0)

5.- FilTrAdo Por rANgoS dE vAlorES
añade otra página al sitio (“range.aspx”) y replica el código de la página patrón, copiando y pegando, para tener ya todo lo básico configurado. ahora arrastra dos controles de texto a la parte de arriba del panel. Nos servirán para filtrar la lista de productos indicando un valor mínimo y máximo para el precio de éstos. en el código hTML incluye estas etiquetas:
<asp:QueryExtender ID=”QueryExtender1” runat=”server” TargetControlID=”LinqDataSource1”> <asp:RangeExpression DataField=”UnitPrice” MinType=”Inclusive” MaxType=”Inclusive”> <asp:ControlParameter ControlID=”TextBox1” DefaultValue=”0” /> <asp:ControlParameter ControlID=”TextBox2” /> </asp:RangeExpression> </asp:QueryExtender>

en el caso de las expresiones de filtrado por rangos debemos indicar por qué campo vamos a filtrar (atributo DataField) y si queremos que los valores mínimo y máximo se incluyan en el rango de valores permitidos (o sea, si es un intervalo abierto o cerrado en cada extremo). Dentro de la expresión incluimos dos parámetros que obtienen sus valores desde un control, para indicar que el valor mínimo lo obtenga del primer cuadro de texto, y el máximo del segundo. además el valor mínimo, si el campo está en blanco, se considerará que es cero (no hay precios negativos).

6.- FilTrAdo Por vAlorES dE ProPiEdAdES
en una nueva página de ejemplo que vamos a llamar “PropertyQe.aspx”, copiaremos el código hTML de la página de búsqueda del apartado 4. La usaremos de base para añadirle una opción adicional de filtrado y decidir si queremos ver sólo los productos descatalogados o no. Pon un control de tipo CheckBox al lado del cuadro de búsqueda. Le he quitado también la ordenación al LinqDataSource, y ahora la haremos con una expresión de ordenación para probarlas. en el marcado del QueryExtender añade este filtro:
<asp:PropertyExpression> <asp:ControlParameter ControlID=”CheckBox1” PropertyName=”Checked” Name=”Discontinued” /> </asp:PropertyExpression> <asp:OrderByExpression DataField=”ProductName” Direction=”Ascending”></asp:OrderByExpression>

ahora, al ejecutar la página verás que puedes filtrar simultáneamente por el texto de búsqueda en el nombre de los productos y de los proveedores, y también decidir si quieres ver sólo los productos descatalogados o no.

Filtrado de datos automático con Queryextender 155

como ejemplo adicional un poco distinto, he creado un filtro que permite seleccionar de una lista desplegable el nombre de un proveedor y que se muestren sólo los productos de éste. a la página de ejemplo le he llamado “PropertyQe2.aspx”. es muy sencillo de hacer. Puedes estudiar el código hTML utilizado mirándolo en el ZIP con los ejemplos del libro.

Figura 6.- Ejemplo de filtro a partir de una lista desplegable

7.- PArÁMETroS dE FilTrAdo
en los ejemplos que acabamos de ver nuestras expresiones tomaban los valores a utilizar para el filtrado desde uno o varios controles dentro de la página. al igual que en el caso de los controles DataSource, que permiten tomar sus parámetros de diversos lugares, las expresiones de filtrado también disponen de la misma funcionalidad. en concreto ofrecen los siguientes orígenes para valores de parámetros: • • • • • • ControlParameter: el que hemos utilizado hasta ahora. obtiene el valor del parámetro a partir de una propiedad de un control de la página. CookieParameter: usa una cookie para sacar el valor para el filtro. FormParameter: un parámetro enviado a la página a través de un PosT de un formulario. ProfileParameter: utiliza un valor definido para el perfil del usuario con la aPI de Profile de asP.NeT. QueryStringParameter: obtiene el valor desde un parámetro pasado en la UrL. RouteParameter: permite extraer el valor para el parámetro a partir de un fragmento de la ruta de la página actual, si estamos usando el sistema de enrutado aparecido en asP.NeT 3.5 (ver capítulo 1). Usado normalmente en combinación con Dynamic Data. SessionParameter: lee el valor de una variable de sesión.

156

tecnologías AsP.net 4.0 (saltando desde la versión 2.0)

como vemos las posibilidades son muchas y podemos usar varios de estos orígenes al mismo tiempo para generar valores para un filtro. en la figura 7 puedes verlos en el editor de visual studio.

Figura 7.- diferentes tipos de parámetros de filtrado

Para hacer un ejemplo con uno de estos orígenes de parámetros adicionales, vamos a modificar la página de búsqueda del apartado 4 (está ya modificada en el ZIP de los ejemplos). añade una columna a la rejilla que genere, para cada registro, un enlace para mostrar los detalles de un producto en otra página. Para ello, desde el editor de columnas de la rejilla, añade una nueva de tipo HyperLinkField, asígnale el texto “ver detalles >>”, el nombre del campo ProductID en la propiedad DataNavigateUrlFields, y la cadena “ ~/DetallesProducto.aspx?ID={0}” en la propiedad DataNavigateUrlFormat. con ello lo que obtenemos es un enlace para cada producto que apuntará a la página “DetallesProducto.aspx”, pasándole el identificador del producto en la UrL. ahora crea una nueva página “DetallesProducto.aspx” y arrastra sobre ella un control LinqDataSource configurado para traerse todos los campos de la tabla de productos, y un control DetailsView enlazado a este origen de datos.
Nota:
Soy consciente de que sería mucho más sencillo y directo usar las propiedades de filtrado propias del LinqDataSource para obtener los datos del producto a partir del valor indicado en la UrL, usando un método tradicional. este ejemplo se hace con un QueryExtender como prueba de concepto de uso de otro tipo de orígenes de filtrado, pero no porque sea la mejor forma de hacerlo en este caso particular.

en el código de la página incluye las siguientes etiquetas justo debajo del control LinqDataSource:
<asp:QueryExtender ID=”QE1” runat=”server” TargetControlID=”LinqDataS ource1”>

Filtrado de datos automático con Queryextender 157

<asp:PropertyExpression> <asp:QueryStringParameter DefaultValue=”0” QueryStringField=”ID” Name=”ProductID” /> </asp:PropertyExpression> </asp:QueryExtender>

en este caso hemos usado una expresión de tipo propiedad, pero en lugar de asignarle el valor de un control, hemos usado un parámetro QueryStringParameter para sacar el identificador del producto de la UrL actual. Le indicamos el nombre del parámetro de la UrL del que sacamos el valor. en este caso también le decimos que use como valor por defecto (si no existe el parámetro ID) un 0, de modo que no se devolverá dato alguno de la base de datos, mostrándose un mensaje por defecto que hemos configurado previamente en el DetailsView.

8.- EN rESuMEN
La nueva funcionalidad de QueryExtender nos brinda la posibilidad de crear listados con múltiples filtros complejos de una manera directa y sin necesidad de escribir código. esta es una de las características, junto con Dynamic Data, que convierten a asP.NeT en la plataforma de desarrollo Web más productiva del mercado para crear interfaces de datos. Descarga los ejemplos de este capítulo, juega con ellos y haz tus propias pruebas para aprender a sacarle todo el partido.

Índice analítico
{{ }}, doble llave <compositescript> <contentTemplate> <ProgressTemplate> <scriptreferences> <scripts> 103 87 32 39 83 72 contentTemplatecontainer css condicionadas 33 99

d
data (propiedad) Datacontext dataProvider (propiedad) Dataview dataview (atributo) Desarrollo Paralelo Disparadores (triggers) Displayafter doble llave {{}} Dynamic Data Dynamiccontrol DynamicDataManager Dynamicentity DynamicField DynamicLayout 95 108 95 95-98 96 6 34-37 40 103 117-146 145 126, 144 129, 146 145 39

A
aDo.NeT Data services adoNetDatacontext aJaX control Toolkit allowcustomerrorredirect asP.NeT Mvc associatedUpdatePanelID asyncPostBackerror asyncPostBackerroreventargs asyncPostBackerrorMessage asyncPostBackTrigger authenticationservice 6 109 50-58 48 7 39 44 45 45 37 76

b
binding 103

E
enablehistory enablePageMethods enablePartialrendering endrequesteventargs enlazado de datos en tiempo real enrutamiento entity Framework eric lawrence 113 64 32, 85 47 103-105 6 5 62
159

c
childrenasTriggers class (atributos) clientscriptManager code (atributos) combinación de scripts confirmButtonextender 34 99 82 101 87 58

160

tecnologías AsP.net 4.0 (saltando desde la versión 2.0)

errores, gestión en aJaX evaluador de expresiones {{}}

43-48 103

M

F

Facebook fetchMethod (propiedad) Fiddler FieldTemplateUsercontrol Filterrepeater

10 95 62 131 128

g

GeT Getcurrent getelementByID getelementsByTagName Gmail GridviewPager

19 43 15 15 10, 11 127

Maestro-detalle, vistas 105 Master Pages 42 Membership, aJaX 75-81 MetaDataType 137 Metadatos (Dynamic Data) 136 MetaModel 122 MetaTable 125 Métodos de página 62 MicrosoftajaxMode 85 Modelo de datos (Dynamic Data) 118, 121-123 Mvc 7

o
observer, patrón 103

h

P
112-115 10

historial del navegador hotmail

i

Interval IsasyncPostback isLoggedIn isUserInrole

41 31 78 81

J
JsoN 11, 21-23

PagerequestManager 47 PageTemplates (carpeta) 125 Paralelo, Desarrollo 6 Plantillas (Dynamic Data) 118, 123-132 Plantillas de campos propias 141-143 Plantillas de lado cliente (concepto) 90-91 Plantillas para campos 129 Plantillas para entidades 129 PosT 19 PostBackTrigger 37 Pseudo-columnas de datos 98

l

Lenguajes dinámicos LinqDatasource Live binding

7 5 103-105

Q
QueryableFilterrepeater Queryextender 128 147

Índice analítico 161

r
registerroutes remote scripting repintado parcial retrollamdas de red roles, aJaX roleservice routing rutas (Dynamic Data) 122 11 30, 33-34 60-67 75-81 76 6 119, 132-135

Timer (control) Triggers (diparadores)

40 34-37

u
UpdateMode UpdatePanel UpdateProgress 33 28, 32-33 38

v
validatorcalloutextender 58

S
savechanges 109 scaffoldallTables 122 scaffoldcolumn 138 scripthandlerFactory 70 scriptManager 28, 31-32, 42-43 scriptManagerProxy 43 scriptservice 69 selectedData 106 services (propiedad scriptManager) 72, 83 servicios de aplicación aJaX 75-81 servicios Web, desde aJaX 67-75 sys (atributos) 96, 100-101 sys:activate 96 sys:attach 97 sys:command 105 sys:commandargument 105 sys:commandtarget 105 sys:key 106 system.Wed.extensions.dll 5 sys-template 94

W
WcF WebserviceProxy Windows cardspace Windows communication Foundation Windows Presentation Foundation Windows Workflow Foundation WPF 3 71 4 3 4 3 4

X
XMLhttprequest 11

T
Tick (evento) 41

You're Reading a Free Preview

Descarga
scribd
/*********** DO NOT ALTER ANYTHING BELOW THIS LINE ! ************/ var s_code=s.t();if(s_code)document.write(s_code)//-->