Autor en Google+
Saltar al contenido

Artículos, tutoriales, trucos, curiosidades, reflexiones y links sobre programación web ASP.NET Core, MVC, Blazor, SignalR, Entity Framework, C#, Azure, Javascript... y lo que venga ;)

15 años online

el blog de José M. Aguilar

Inicio El autor Contactar

Artículos, tutoriales, trucos, curiosidades, reflexiones y links sobre programación web
ASP.NET Core, MVC, Blazor, SignalR, Entity Framework, C#, Azure, Javascript...

¡Microsoft MVP!
Mostrando entradas con la etiqueta ajax. Mostrar todas las entradas
Mostrando entradas con la etiqueta ajax. Mostrar todas las entradas
martes, 17 de enero de 2012
Uau!!Una aplicación que mezcla internet, asincronía, y múltiples usuarios colaborando e interactuando al mismo tiempo siempre es merecedora de un “¡uau!”. Seguro que, al igual que un servidor, en algún momento os habéis quedado maravillados con la interactividad que presentan algunos sistemas web modernos, como Facebook, Google Docs, o muchos otros, en las que estamos recibiendo actualizaciones, prácticamente en tiempo real, sin necesidad de recargar la página.

Por ejemplo, en Google Docs, si estamos editando un documento online y otro usuario accede al mismo, podemos ver sobre la marcha que ha entrado, e incluso las modificaciones que va realizando sobre el documento. O algo más cotidiano, en un simple chat vía web van apareciendo los mensajes tecleados por nuestros compañeros de sala como por arte de magia. Ambos sistemas utilizan el mismo tipo de solución: el envío asíncrono de datos entre servidor y clientes en tiempo real.

En esta serie de artículos veremos cómo podemos implementar sorprendentes funcionalidades de este tipo utilizando SignalR, un framework open source desarrollado por gente del equipo de ASP.NET, que nos facilitará bastante la tarea.

Estos contenidos se publican bajo una licencia de Creative Commons Licencia Reconocimiento-No comercial-Compartir bajo la misma licencia 3.0 España de Creative Commons

miércoles, 24 de marzo de 2010
jQuery Sí, habéis leído bien: ASP a secas, sin la habitual terminación “.NET” que venimos usando mucho últimamente ;-).

Resulta que estoy metido en un proyecto colaborando con un equipo de trabajo que está desarrollando un portal web con esa tecnología (!), y me han encargado la creación de unas herramientas ajaxificadas dentro del mismo.

Aunque pueda parecer lo contrario, las aplicaciones desarrolladas con las vetustas (pero aún vivas) páginas activas de servidor (ASP) pueden aprovechar toda la potencia de las librerías de script de última generación, como la maravillosa jQuery. Vamos a ver lo sencillo que resulta tanto implementar un servicio JSON como consumirlo desde una página ASP.

El lado servidor

El servicio JSON lo implementaremos en un archivo al que llamaremos, por ejemplo, JsonObtenerProductos.asp, cuyo código podría ser como el siguiente:

<!--#INCLUDE FILE="Includes\Functions.asp" -->
<%
   Dim categoryId, Rs, count
 
   Response.Expires = -1
   If Not UserLogged() or Request("categoryId")="" Then
      Response.End
   End If
 
   categoryId = Cint(Request("categoryId"))
 
   OpenDatabase()
   Set Rs = GetProductsByCategoryRecordset(categoryId)
 
   Response.ContentType = "application/json"
   Response.Write "{"
   Response.Write "  ""datos"": ["
   count = 0
 
   While Not Rs.Eof
      Dim id, name, desc
      id = Rs("id")
      name = Rs("name")
      desc = Rs("desc")
      count = count + 1
      If count > 1 Then
         Response.Write ", "
      End If
 
      Response.Write "{ "
      Response.Write "   ""productId"": " & id & ", "
      Response.Write "   ""productName"": """ & name & """, "
      Response.Write "   ""productDesc"": """ & desc & """"
      Response.Write "}"
      Rs.MoveNext      
   Wend
   Response.Write "         ]"
   Response.Write "}"
 
   Rs.Close
   CloseDatabase()
%>

Como se observa, se establece el tipo de contenido a “application/json”, y a continuación se envía al cliente un objeto en formato JSON, en este caso un array de objetos construidos a partir de la información obtenida desde un RecordSet.

Así, una llamada al servicio mediante la petición GET /JsonObtenerProductos.asp?categoryId=3 podría retornar el siguiente resultado:

{  
   "datos": 
   [
      { "productId": 1, "productName": "Producto 1", "productDesc": "Descripción 1"}, 
      { "productId": 2, "productName": "Producto 2", "productDesc": "Descripción 2"}, 
      { "productId": 3, "productName": "Producto 3", "productDesc": "Descripción 3"}
   ]
}

Se puede entender a simple vista; se trata de una serie de objetos, separados por comas, en cuyo interior se definen cada una de sus propiedades con su correspondiente valor. Fijaos que el mismo nombre de la propiedad va entrecomillado.

El lado cliente

Desde la página .asp que consumirá el servicio, como siempre, lo primero es referenciar la librería jQuery en la página, así:

<script src="Scripts/jquery-1.4.2.min.js" type="text/javascript"></script>

A continuación, introducimos en el marcado de la página el siguiente código, que muestra un desplegable con categorías de productos, cuyo cambio provocará la llamada al servidor, y reserva un bloque <div> para mostrar el resultado:

<select id="categories" onchange="reloadProducts()">
   <option value="1">Electrodomésticos</option>
   <option value="2">Informática</option>
   <option value="3">Cocina</option>
   ...
</select>
<div id="result">
</div>

El código de la función reloadProducts() es el mostrado a continuación. Utiliza el método getJSON() de jQuery para realizar la llamada Ajax, suministrándole como parámetro la categoría seleccionada en el desplegable en forma de objeto anónimo.

<script type="text/javascript">
  function reloadProducts() {
    $.getJSON(       
    "JsonObtenerProductos.asp",               // URL del servicio
    { categoryId: $("#categories").val() },   // Parámetros
    function(data) {                          // Función callback          
      var elem = $("#result");elem.empty(); // Limpiamos el contenedor          
      var s = "<ul>";
      $.each(data.datos, function(i, item) {
        s += "<li>" + item.productName + "</li>";
      });
      s += "</ul>";
      elem.html(s);   // Insertamos la lista en el contenedor
    });
  }
</script>

En la función callback, invocada por jQuery cuando la llamada Ajax ha sido completada con éxito, recibimos en el parámetro “data” los datos obtenidos desde el servidor, en nuestro caso un array de objetos.

Tras limpiar el <div> donde depositaremos la información, recorremos con $.each() el array de objetos data.datos para crear una lista de productos, que insertamos a continuación en el interior del contenedor.Observad que en el interior del bucle estamos accediendo directamente a la propiedad “productName” del elemento.

Y voila! Con esto tendríamos listo un sistema completo de carga asíncrona de elementos utilizando ASP y jQuery. Pero no sólo eso, podríamos utilizar la misma técnica para realizar actualizaciones de zonas de página definidas en archivos .asp independientes, o aprovechar la potencia de plugins y librerías adicionales para mejorar la experiencia de usuario de nuestros sistemas.

Y es que ASP todavía se utiliza bastante y, aunque se trate de una tecnología obsoleta, puede resultar totalmente válida para crear aplicaciones actuales desde el punto de vista de interfaces de usuario e interacción con el servidor, utilizando librerías de scripts de última hornada.

Publicado en: Variable not found.
Hey, ¡estoy en twitter!

Estos contenidos se publican bajo una licencia de Creative Commons Licencia Reconocimiento-No comercial-Compartir bajo la misma licencia 3.0 España de Creative Commons

lunes, 1 de febrero de 2010
aspnetmvc Con objeto de mejorar la seguridad de nuestras aplicaciones, la Release Candidate de ASP.NET MVC 2 introdujo un cambio importante en la forma de procesar peticiones que retornan información serializada como JSON: por defecto, ahora sólo se responde a peticiones de tipo POST.

Dado que en MVC 1.0 era justo al contrario, esta pequeña reorientación hace que aplicaciones que antes funcionaban correctamente dejen de hacerlo al migrarlas a la última versión del framework. Un ejemplo lo tenemos con las aplicaciones que aprovechan la potencia de jqGrid para mostrar y editar rejillas de datos. Recordemos que el intercambio de información entre cliente y servidor se realiza mediante llamadas Ajax que retornan siempre datos en notación JSON.

Los síntomas que notaremos en estos casos son muy simples: ¡las acciones que deberían devolvernos información dejan de hacerlo! En algunas ocasiones, dependiendo del uso que se dé en la vista a la la información retornada, es posible que aparezcan errores de script en el navegador, pero otras ni siquiera eso.

En cualquier caso, no es difícil dar con la solución. Con Firebug, Fiddler, o cualquier herramienta que nos permita monitorizar las peticiones, podremos observar que en la petición Ajax se está produciendo un error HTTP 500 (error interno de servidor):

image

Y si seguimos profundizando en dicha petición, podemos ahora conocer el mensaje descriptivo que envía el servidor, un auténtico detalle por parte del equipo de desarrollo de ASP.NET MVC 2, en el que incluso nos indican la solución al problema:

<html>
<head>
<title>This request has been blocked because sensitive 
information could be disclosed to third party web sites when 
this is used in a GET request. 
To allow GET requests, set JsonRequestBehavior to AllowGet.</title>
<style>

Ante esta situación, podemos optar por dos soluciones. La primera sería indicar explícitamente en la instancia del resultado, de tipo JsonResult, que queremos permitir el método GET, así:

return Json(data, JsonRequestBehavior.AllowGet);

Y otra posibilidad sería realizar las peticiones Ajax utilizando el método POST, algo que podemos conseguir muy fácilmente la mayoría de las veces. En el caso de los ejemplos con jqGrid, sería sustituyendo el ’GET’ por ‘POST’ en código:

[...]
jQuery("#list").jqGrid({
url: '<%= Url.Action("ObtenerDatosGrid") %>',
datatype: 'json',
mtype: 'POST',    // <- Aquí
colNames: ['Apellidos', 'Nombre', 'Fecha Nac.', 'Email'],
[...]

¡Gracias, Maxi, por los comentarios que inspiraron este post!

Publicado en: Variable not found.

Estos contenidos se publican bajo una licencia de Creative Commons Licencia Reconocimiento-No comercial-Compartir bajo la misma licencia 3.0 España de Creative Commons

miércoles, 16 de septiembre de 2009

Logo de ASP.NET Ajax Hace unos meses comentaba la posibilidad de utilizar la infraestructura de Google para alojar las librerías javascript de nuestras aplicaciones. Pues bien, ahora es Microsoft la que ha lanzado un servicio similar, Microsoft Ajax CDN, una red de distribución de contenidos desde donde podemos descargar en tiempo de ejecución las librerías de scripts que utilicemos en nuestras aplicaciones.

O en otras palabras, que podemos hacer uso de forma gratuita de estas librerías, sin limitación de ancho de banda e independientemente de si es para fines comerciales o no. Basta con referenciarlas desde nuestro código:

<script src="http://ajax.microsoft.com/ajax/jquery-1.3.2.min.js" 
type="text/javascript"></script>

La principal ventaja que ofrece este método es la velocidad con la que estos archivos serán servidos, puesto que se usa la infraestructura del gigante de Redmon, a la vez que se comparte la caché con otros sitios web que también las estén utilizando. También brinda la posibilidad de utilizar scripting a sitios web que no disponen de permisos para subir archivos (como la plataforma blogger)

A diferencia del servicio de Google, desde esta CDN sólo podemos encontrar de momento las librerías que oficialmente forman parte de la plataforma de desarrollo de Microsoft, como las propias de ASP.NET Ajax, jQuery y aquellos plugins que vayan añadiéndose. En la dirección http://www.asp.net/ajax/cdn/ pueden consultarse la lista completa de librerías, con sus correspondientes direcciones de descarga.EnableCdn en ScriptManager

Adicionalmente, Scott Guthrie comentaba en su blog que el nuevo control ScriptManager que vendrá con ASP.NET 4.0 incluye una propiedad llamada EnableCdn que permitirá activar la descarga de las librerías Ajax y todas aquellas necesarias para el funcionamiento de controles, directamente desde sus servidores.

Los inconvenientes, pues los mismos que los del servicio de Google: si no disponemos de conexión a la red en tiempo de desarrollo, podemos tenerlo realmente crudo.

Más información en: www.asp.net/ajax/cdn

Publicado en: Variable not found.

Estos contenidos se publican bajo una licencia de Creative Commons Licencia Reconocimiento-No comercial-Compartir bajo la misma licencia 3.0 España de Creative Commons

domingo, 25 de enero de 2009
Ajax API PlaygroundEl pasado miércoles, Ben Lisbakken descubría en el Blog de Google Code el proyecto al que había dedicado el famoso (¿y difunto?) 20% de la jornada laboral en esta compañía: AJAX APIs Playground.

Se trata de un sitio web interactivo en el que se encuentran un total de 170 ejemplos de uso de las siguientes API de Google:
  • API de visualización, que permite a los desarrolladores acceder a datos estructurados y mostrarlos en una gran variedad de formatos, como tablas o gráficos estadísticos.
  • API Ajax para búsquedas, que facilita la incorporación de capacidades de búsqueda de cualquier tipo (páginas, direcciones, multimedia, etc.) en sitios web utilizando javascript.
  • API Ajax de idioma, que nos permite acceder mediante javascript a las herramientas de detección de idioma, traducción y transliteración de Google.
  • API de datos de Blogger, que permite el acceso con Ajax a información sobre blogs, posts y comentarios de esta plataforma de publicación.
  • API de bibliotecas Ajax, la red de distribución de librerías estándar como jQuery, jQuery UI, Prototype, script.aculo.us, MooTools o Dojo.
  • API de Google Maps, con el que podremos integrar Google Maps en nuestras webs y utilizar los servicios de localización y posicionamiento que nos ofrece.
  • API de Google Earth, el cual nos facilita la inclusión del sistema Google Earth en webs, así como la interacción con ellos vía javascript.
  • API Ajax para feeds, un conjunto de funciones que nos permiten obtener feeds de otras páginas sin necesidad de crear proxies de servidor.
  • API de Google Calendar, el interfaz a través del cual podemos crear aplicaciones totalmente integradas con Google Calendar.

Pero, al menos para mí, lo mejor viene ahora: dispone de un editor de código fuente en el que podemos modificar dichos ejemplos, ejecutarlos sobre la marcha e incluso guardar o exportar los cambios que realicemos, por lo que resulta de lo más didáctico y efectivo para hacernos con el manejo de estas potentes herramientas.

Enlace: Ajax API Playground.
Publicado en: www.variablenotfound.com.

Estos contenidos se publican bajo una licencia de Creative Commons Licencia Reconocimiento-No comercial-Compartir bajo la misma licencia 3.0 España de Creative Commons

domingo, 13 de julio de 2008
Cuestiones enviadas por lectoresHace unos días Pedro dejaba una consulta en los comentarios del post "Usando ASP.NET Ajax para el intercambio de entidades de datos" sobre un problema que le había surgido a la hora de referenciar desde script, en el lado cliente, una clase propia que utilizaba para intercambiar datos entre éste y el servidor.

En dicho post se mostraba la forma en que era posible intercambiar información estructurada con Ajax, definiendo en el servidor una clase propia y viendo cómo el ScriptManager, mágicamente, creaba un proxy (o espejo) en cliente que permitía su manipulación de forma muy cómoda y transparente al otro lado de la red.

De hecho, partíamos de una definición en el servidor así:
  public class Mensaje
{
public string Remitente;
public string Destinatario;
public DateTime Fecha;
public int Numero;
}
 
Y veíamos como desde cliente podíamos manipularla, en javascript, de la siguiente forma:
  msg = new Mensaje();
msg.Remitente = $get("nombre").value;
msg.Numero = 1;
msg.Destinatario = "servidor";
msg.Fecha = new Date();
 
El problema que comentaba este lector es que, a diferencia del ejemplo, su entidad de datos se encontraba definida en un ensamblado y espacio de nombres diferente al del WebMethod que lo utilizaba, lo que provocaba la aparición de un error indicando que su clase no estaba definida.

Aunque al principio sospeché en que podía existir alguna limitación en la seriación JSON de los datos, después de indagar un poco dí con la solución. La lección que he aprendido es:
para usar desde cliente una clase generada de forma automática por el ScriptManager, es necesario referenciarla precedida del namespace en el que se encuentra definida
O en otras palabras, si la clase Mensaje está definida dentro del espacio de nombres A.B, la referencia en cliente deberá ser:
  var x = new A.B.Mensaje();
 ¿Y por qué funciona bien el ejemplo AjaxPingPong, si la referencia a la clase Mensaje no incluía su espacio de nombres? Pues debido a que estaba definida en el namespace por defecto del proyecto...

¡Gracias, Pedro, por participar en Variable Not Found!

Publicado en: www.variablenotfound.com.

Estos contenidos se publican bajo una licencia de Creative Commons Licencia Reconocimiento-No comercial-Compartir bajo la misma licencia 3.0 España de Creative Commons

domingo, 1 de junio de 2008
[ING] Ir al sitio web de jQueryMuy interesante el artículo Using jQuery to directly call ASP.NET AJAX page methods, en el que se demuestra que es posible, y además realmente sencillo, invocar métodos estáticos de página (PageMethods) utilizando esta magnífica librería javascript.

Como ya sabemos los PageMethods son métodos estáticos definidos dentro de la clase de una página, es decir, en su codebehind, y que son accesibles desde cliente utilizando Ajax (en minúsculas!).

Hace más de un año ya estuve comentando cómo hacerlo utilizando la propia infraestructura de ASP.NET Ajax y sus librerías de scripting para hacerlo muy fácilmente. De forma muy breve, todo consistía en crear el método estático al que se deseaba acceder, decorarlo con el atributo WebMethod, e incluir en la página un ScriptManager con estableciéndole la propiedad EnablePageMethods=true; a partir de ese momento, podíamos invocarlo desde cliente usando javascript de forma muy directa.

Sin embargo, el uso de estándares como JSON hace posible la invocación de WebMethods desde scripting sin necesidad de recurrir a la magia del ScriptManager. De hecho, y como vamos a ver más adelante, vamos a realizar la llamada al método utilizando Ajax desde una página HTML pura, sin controles de servidor ni otros edulcorantes proporcionados por ASP.NET.

Desarrollaremos un ejemplo completo (esta vez en VB.NET, por cambiar un poco), que demuestre cómo podemos crear un PageMethod en una página (default.aspx), e invocarlo desde otra (pagina.htm) usando la librería jQuery para comunicar ambos componentes.

1. Definición del PageMethod

Como ya comenté en el post al que hacía referencia antes, un PageMethod se define en la clase asociada a una página .aspx (su code-behind) y es un método normal y corriente, aunque obligatoriamente será estático y público. Además, debe presentar el atributo WebMethod(), que lo identifica como accesible a través llamadas HTTP.

El siguiente código muestra el PageMethod que vamos a utilizar. Se supone que partimos de una página Default.aspx totalmente vacía (de hecho, no vamos a crear nada más dentro), y que el siguiente código corresponde al archivo Default.aspx.vb:
Imports System.Web.Services

Partial Public Class _Default
Inherits System.Web.UI.Page

<WebMethod()> _
Public Shared Function Saludame(ByVal Nombre As String) As String
Return "Hola a " + Nombre _
+ " desde el servidor, son las " _
+ DateTime.Now.ToString("hh:mm:ss")

End Function
End Class
 
Se puede observar que el método recibirá un parámetro, el nombre del destinatario del mensaje de saludo. Una vez que lo tenemos definido de esta forma, siempre que el proyecto esté correctamente configurado, el método es accesible en la dirección "Default.aspx/Saludame" para todo aquél que quiera utilizarlo, siempre que siga ciertas reglas.

2. Creamos la página "cliente"

Interfaz del ejemploPara probar la invocación al método definido anteriormente montaremos un interfaz muy simple, un cuadro de edición en el que el usuario introducirá un nombre y un botón que solicitará al servidor el saludo. Las sucesivas respuestas se irán añadiendo al final de la página, tras la etiqueta "Mensajes obtenidos".

En lugar de mostrar el código fuente de la página HTML (pagina.html) completo, voy a hacerlo por partes, y saltándome algunas porciones que no tienen demasiado interés. En primer lugar va la correspondiente al interfaz, que es bastante simple:
<body>
<form>
<input type="text" id="nombre" />
<input type="button" value="¡Pulsa!"
onclick="llamar();" />
<br />
<strong>Mensajes obtenidos:</strong><br />
<label id="lblMensajes" />
</form>
</body>
 
Como se puede intuir, la función llamar(), invocada al hacer click sobre el botón, será la que realice la conexión con el servidor, le envíe el nombre contenido en el cuadro de edición, e introduzca la respuesta en la etiqueta lblMensajes, que actuará como contenedor.

Vamos ahora con la porción de página donde se desarrolla la acción. Primero se incluye jQuery en la página, y acto seguido creamos la función llamar() que hemos comentado anteriormente:
<script type="text/javascript" 
src="scripts/jquery-1.2.6.min.js" ></script>
<script type="text/javascript">
function llamar()
{
$.ajax({
type: "POST",
data: "{ Nombre: '" + $("#nombre").val() + "'}" ,
url: "Default.aspx/Saludame",
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function(msg) {
$("#lblMensajes").append(msg);
$("#lblMensajes").append("<br />");
},
error: function(msg) { alert("Algún problema debe haber..."); }
});
}
</script>
 
Los parámetros de llamada a la función ajax() de jQuery son los siguientes:
  • type, la petición HTTP es de tipo POST. Es obligatorio para acceder a los PageMethods por motivos de seguridad.
  • data contiene una representación JSON de los parámetros enviados al método. En este caso, vemos que se compone de forma dinámica inyectándole el valor del cuadro de edición (obtenido a su vez utilizando un selector jQuery y el método val()). Fijaos que esta representación define un objeto anónimo cuyas propiedades deberán coincidir con los parámetros del método; para este ejemplo, un valor correcto de data sería "{ Nombre: 'Juan' }".
  • url contiene la dirección del método. Observad que es el nombre de la página seguido del nombre del método.
  • El content-type indica el tipo de contenido que enviamos al servidor; en este caso, dado que le vamos a enviar JSON codificado en utf-8 debe ser "application/json; charset=utf-8".
  • Con dataType indicamos al servidor qué tipo de respuesta estamos esperando. Dado que la trataremos también desde script, será más sencillo si es JSON.
  • success contiene una función anónima que recibe como parámetro la respuesta del servidor. En este caso, hemos implementado su inclusión en la etiqueta que habíamos preparado para mostrar los mensajes del servidor.
  • de la misma forma, en error implementamos lo que queremos que haga el sistema cuando se produzca un problema. En este caso sólo avisamos.

Y... ¡listo para funcionar!

Fijaos en un detalle importante: nada impide la invocación de PageMethods desde páginas distintas a donde se han definido, cosa que con el comportamiento estándar del ScriptManager y su EnablePageMethods=true sería imposible.

Finalmente, recordar que para que todo esto funcione el proyecto web debe estar correctamente configurado para trabajar con las extensiones Ajax. Las plantillas de proyectos de Visual Studio 2008 ya vienen preparadas de forma predeterminada, pero si usas VS2005 deberás crear el proyecto usando las plantillas para aplicaciones Web con Ajax, o retocar a mano el web.config.

Puedes descargar el proyecto de prueba para VS2005.

Publicado en: www.variablenotfound.com.

Estos contenidos se publican bajo una licencia de Creative Commons Licencia Reconocimiento-No comercial-Compartir bajo la misma licencia 3.0 España de Creative Commons

martes, 29 de abril de 2008
De nuevo en ASP.NET Resources encuentro una magnífica recopilación, en forma de chuletas de consulta rápida, de las librerías javascript disponibles en cliente usando ASP.NET Ajax. Puedes descargar el archivo pulsando sobre la imagen:

Descargar compilación en formato .zip

El archivo distribuido, un zip, contiene siete chuletas en formato PDF:
  • Extensiones a los tipos String y Object
  • Extensiones a los tipos Number y Error
  • Referencia del tipo DomEvent
  • Extensiones al tipo DomElement
  • Extensiones a los tipos Date y Boolean
  • Eventos del ciclo de vida en cliente
  • Extensiones al tipo Array
ScottGu ya le dio difusión a través de su blog hace más de un año, así que probablemente no sea nada nuevo para muchos, pero para mí ha sido todo un descubrimiento.

Publicado en: www.variablenotfound.com.

Estos contenidos se publican bajo una licencia de Creative Commons Licencia Reconocimiento-No comercial-Compartir bajo la misma licencia 3.0 España de Creative Commons

domingo, 20 de abril de 2008
jQueryA la vista de la cantidad de posts que se están escribiendo al respecto y del entusiasmo que despierta su utilización, parece claro que jQuery se está erigiendo como un interesantísimo complemento para el framework MVC de Microsoft.

jQuery, para que el no haya oído hablar de ella, es una librería Javascript destinada a facilitar enormemente la vida a los desarrolladores simplificando y unificando el manejo de eventos, la manipulación del contenido (DOM), estilos, el uso de Ajax, la creación de animaciones y efectos gráficos, y un larguísimo etcétera propiciado por la facilidad para añadirle plugins que extienden sus funcionalidades iniciales. Y todo ello de forma muy rápida, sin excesivas complicaciones, y sin añadir demasiado peso a las páginas.

En este post vamos a ver un ejemplo de integración de jQuery con ASP.NET MVC framework realizando una aplicación muy sencilla e ilustrativa que nos enseñará cómo enviar información desde el cliente al servidor y actualizar porciones de página completas con el retorno de éste, respetando en todo momento la filosofía MVC.

El funcionamiento será realmente simple: el usuario introduce su nombre y edad, y al pulsar el botón se enviará esta información al servidor, que la utiliza para componer una respuesta y mandarla de vuelta al cliente. Cuando éste la recibe, la mostrará (con un poco de 'magia' visual de jQuery) y transcurridos unos segundos, desaparecerá de forma automática. La siguiente captura muestra el sistema que vamos a construir en ejecución:

MVC-jQuery en ejecución

Pero antes de entrar en faena, unos comentarios. En primer lugar, sólo voy a explicar los aspectos de interés para la realización del ejemplo, partiendo de las plantillas adaptadas para Web Developer Express 2008. Si prefieres antes una introducción sobre el framework, puedes visitar las magníficas traducciones de Thinking in .Net de los tutoriales de Scott Guthrië sobre MVC. Se refieren a la primera preview, pero los fundamentos son igualmente válidos.

Segundo, supongo que funcionará con versiones superiores de Visual Studio, pero no he podido comprobarlo. Está creado y comprobado con Visual Web Developer Express, y la Preview 2 del framework MVC.

Y por último, decir que el ejemplo completo podrás descargarlo usando el enlace que encontrarás al final del post. :-)

Primero: Estructuramos la solución

En líneas generales, nuestra aplicación tendrá los siguientes componentes:
  • Tendremos un controlador principal, llamado Home. En él crearemos dos acciones, las dos únicas que permite nuestra aplicación: una, llamada Index, que se encargará de mostrar la página inicial del sistema, y otra llamada Welcome, que a partir de los datos introducidos por el usuario maquetará el interfaz del mensaje de saludo.

  • Como consecuencia del punto anterior, dispondremos de dos vistas. La primera, Index compondrá la interfaz principal con el formulario, y la segunda, que llamaremos Welcome, que define la interfaz del saludo al usuario (el recuadro de color amarillo chillón ;-)).

    Esta última vista necesitará los datos de la persona (nombre y edad) para poder mostrar correctamente su mensaje, por lo que el controlador deberá enviárselos después de obtenerlos de los parámetros de la petición.
    Fijaos que respetamos en todo momento el patrón MVC haciendo que el cliente invoque a la acción Welcome del controlador usando Ajax, y que la composición del interfaz (HTML) se realice a través de la vista correspondiente. Utilizaremos, por tanto, toda la infraestructura del framework MVC, sin modificaciones.

  • También, para añadir algo de emoción, he incluido una página maestra, que definirá el interfaz general de las páginas del sistema y realizará la inclusión de los archivos adicionales necesarios, como las hojas de estilo y scripts como jQuery.

Segundo: implementamos el controlador

El controlador de nuestra aplicación va a ser bien simple. Lo vemos y comentamos seguidamente:
public class HomeController : Controller
{
public void Index()
{
RenderView("Index");
}

public void Welcome(string name, int age)
{
Person person =
new Person { Age = age, Name = name };

RenderView("Welcome", person);
}
}

public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
 
Podemos observar la clase HomeController que implementa las acciones del controlador Home. La acción Index provoca la visualización de la vista de su mismo nombre.

La acción Welcome, es algo más compleja; en primer lugar, se observa que recibe dos parámetros, en nombre y edad del usuario, que utiliza para montar un objeto de tipo Person, definido algo más abajo, que posteriormente envía a la vista a la hora de renderizarla. Habréis observado aquí la utilización de inicializadores de objetos en la instanciación, y de propiedades automáticas en la definición del tipo.

Tercero: implementamos las vistas

Recordemos que vamos a implementar dos vistas, una para la página principal (llamada Index), que mostrará el formulario de entrada de datos, y otra (que llamaremos Welcome) que definirá el interfaz de la respuesta del servidor. Comenzaremos describiendo cómo incluir jQuery en nuestras páginas, y pasaremos después a ellas.

3.1. Inclusión de jQuery

Para implementar las vistas necesitamos antes preparar la infraestructura. En primer lugar, descargamos jQuery desde la web oficial del proyecto, y la introducimos en nuestro proyecto. Si vas a descargar la solución completa desde aquí, en ella ya viene incluido el archivo.

A continuación, es un buen momento para modificar la página maestra e incluir en ella la referencia a esta librería:
    <script  type="text/javascript" 
src="/scripts/jquery-1.2.3.min.js">
</script>
 
Un inciso importante aquí. Hace unas semanas se publicó un HotFix para Visual Studio 2008 y Web Developer Express que corrije, entre otras, deficiencias en el intellisense y hacen posible el uso de esta magnífica ayuda cuando escribimos código jQuery. Altamente recomendable, pues, instalarse esta actualización.

Sin embargo, el hecho de introducir en la página maestra la referencia a la librería jQuery hace que el intellisense no funcione como debe. Por tanto, aunque no es la opción que he elegido en este proyecto, podríais incluir el script directamente en la vista Index en lugar de en la Master, y así disfrutaréis del soporte a la codificación.

3.2. La vista "Index"

Esta vista será la encargada de mostrar el formulario e implementar la lógica de cliente necesaria para obtener los datos del usuario, enviarlos al servidor y actualizar el interfaz con el retorno. Su implementación se encuentra en el archivo Index.aspx.

Desde el punto de vista del interfaz de usuario es bastante simple, lo justo para mostrar un par de inputs con sus correspondientes etiquetas y el botón que iniciará la fiesta llamando a la función send():
<form action="" id="myForm">
<label for="name">Name: </label><input type="text" id="name" />
<br />
<label for="age">Age: </label><input type="text" id="age" size="2" />
<br />
<button id="btn" onclick="send(); return false;">Send!</button>
</form>
<hr />
<div id="result" style="display: none; width: 400px"></div>
 
Observad que no es necesario establecer un action en el formulario, ni otros de los atributos habituales, pues éste no se enviará (de hecho, el formulario incluso sería innecesario). Fijaos también que el evento onclick del botón retorna false, para evitar que se produzca un postback (¡aaargh, palabra maldita! ;-)) del formulario completo.

Puede verse también un div llamado "result", inicialmente invisible, que se utilizará de contenedor para mostrar las respuestas obtenidas desde el servidor.

Pasemos ahora al script. La función send() invocada como consecuencia de la pulsación del botón pinta así:
   function send()
{
var name = document.getElementById("name").value;
var age = document.getElementById("age").value;
updateServerText(name, age);
}
 
En realidad no hace gran cosa: obtiene el valor de los textboxes y llama a la función que realmente hace el trabajo duro. Este hubiera sido un buen sitio para poner validadores, pero eso os lo dejo de deberes ;-).

El código de la función updateServerText() es el siguiente:
   function updateServerText(name, age)
{
document.getElementById("btn").disabled = true;

$.ajax({
cache: false,
url: '<%= Url.Action("Welcome", "Home") %>',
data: {
Name: name,
Age: age
},
success: function(msg) {
$("#result").html(msg).show("slow");
},
error: function(msg) {
$("#result").html("Bad parameters!").show("slow");
}
});

setTimeout(function () {
document.getElementById("btn").disabled = false;
$("#result").hide("slow");
}, 3000);
}
 
En primer lugar, se desactiva el botón de envío para evitar nuevas pulsaciones hasta que nos interese. He utilizado un método habitual del DOM, getElementById() para conseguirlo, no encontré una alternativa mejor en jQuery.

A continuación se utiliza el método ajax de jQuery para realizar la llamada al servidor. Aunque existen otras alternativas de más alto nivel y por tanto más fáciles de utilizar, elegí esta para tener más control sobre lo que envío, la forma de hacerlo y la respuesta.

Los parámetros utilizados en la llamada a $.ajax son:
  • cache, con el que forzamos la anulación de caché, obligando a que cada llamada se ejecute totalmente, sin utilizar el contenido almacenado en cliente. Internamente, jQuery añade al querystring un parámetro aleatorio, con lo que consigue que cada llamada sea única.

  • url, la dirección de invocación de la acción, que se genera utilizando el método de ayuda Url.Action(), pasándole como parámetros el controlador y la acción, lo que retornará la URL asociada en el sistema de rutas definido. En condiciones normales, si la aplicación se ejecuta sobre el raíz del servidor web, se traducirá por '/Home/Welcome'.

  • data, los datos a enviar, que se establecen en formato JSON, donde cada propiedad va seguida de su valor. jQuery tomará estos valores y los transformará en los parámetros que necesita la acción Welcome, por lo que el nombre de las propiedades deberá corresponder con los parámetros que espera esta acción (Name y Age).

  • sucess define la función de retorno exitoso, que mostrará los datos recibidos del servidor introduciéndolos en el contenedor "result". Y ya que estamos, gracias a la magia de jQuery, se mostrará con un efecto visual muy majo.

  • error, define una función de captura de errores para casos extraños, por si todo falla. Por ejemplo, dado que no estamos validando la entrada del usuario, si éste introduce texto en la edad, el framework no será capaz de realizar la conversión para pasarle los parámetros al controlador y fallará estrepitosamente; en este caso simplemente mostraremos un mensaje de error en cliente.

Fijaos que la llamada a la acción (Welcome) del controlador (Home) es capturada por el framework y dirigida al método correspondiente sin necesidad de hacer nada más, dado que se trata de una llamada HTTP normal. De hecho, si sobre el navegador se introduce la dirección "http://localhost:[tupuerto]/Home/Welcome?Name=Peter&Age=12" podremos ver en pantalla el mismo resultado que recibirá la llamada Ajax.



Obviamente, este efecto podría ser controlado y hacer que sólo se respondieran peticiones originadas a través de Ajax y similares.

Por último, continando con el código anterior, dejamos programado un timer para que unos segundos más tarde, el mensaje mostrado, sea cual sea el resultado de la llamada Ajax, desaparezca lentamente y, de paso, se active de nuevo el botón de envío. El efecto, ya lo veréis si ejecutáis la solución, es de lo más vistoso, creando una sensación de interactividad y dinamismo muy a lo 2.0 que está tan de moda.

3.3. La vista "Welcome"

En esta vista definiremos el interfaz del mensaje que mostraremos al usuario cuando introduzca su información y pulse el botón de envío. Dado que estamos usando MVC, la llamada Ajax descrita anteriormente llegará al controlador y éste hará que la vista cree el interfaz que será devuelto al cliente.

La vista, por tanto, es como cualquier otra, salvo algunas diferencias interesantes. Por ejemplo, no tiene página maestra, no la necesita; de hecho ni siquiera tiene la estructura de una página HTML completa, sólo de la porción que necesita para montar su representación. El código de Welcome.aspx, salvo las obligatorias directivas iniciales, es:
<div style="background-color: Yellow; border: 1px solid black;">
<em>Message from server (<%=DateTime.Now %>):</em><br />
Hi, <%= ViewData.Name %>, your age is <%= ViewData.Age %>
</div>
 
Pero aún queda un detalle que afinar. En el archivo code-behind (o codefile) donde se define la clase Welcome hay que indicar expresamente que la clase es una vista de un tipo concreto de la siguiente forma:
  public partial class Views_WebParts_Welcome : ViewPage<Person>
{
}
 
De esta forma indicamos que los datos de la vista son del tipo Person, lo que nos permite beneficiarnos del tipado fuerte en la composición de la misma; de hecho, esto es lo que permite que podamos usar tan alegremente una expresión como ViewData.Age a la hora de componer el interfaz.

Fijaos que aunque en este ejemplo no hemos hecho ninguna composición compleja y los datos que hemos usado, contenidos en ViewData, han sido obtenidos por el Controlador directamente de la vista, sería exactamente igual si se tratara de algo menos simple, como una página concreta de datos obtenidos desde el Modelo, por ejemplo con Linq, y mostrados en forma de grid.

Cuarto: recapitulamos

Hemos visto, paso a paso, un ejemplo de cómo podemos utilizar el framework MVC de Microsoft para el desarrollo de aplicaciones web que hacen uso de Ajax, utilizando para ello la excelente librería de scripting jQuery.

Para ello hemos creado una vista que es la página Web con el formulario principal, y otra vista parcial con el fragmento compuesto por el servidor con la información recibida. El controlador, por su parte, incluye acciones para responder a las peticiones del cliente independientemente de si se originan a través de Ajax o mediante la navegación del usuario, mostrando la vista oportuna.

La modificación dinámica del interfaz, así como las llamadas asíncronas al servidor, encajan perfectamente en la filosofía MVC teniendo en cuenta algunas reglas básicas, como el respeto a las responsabilidades de cada capa.

Y ahora, lo prometido:

   Descargar el proyecto Descargar proyecto (Visual Web Developer Express 2008).

Publicado en: www.variablenotfound.com.

Estos contenidos se publican bajo una licencia de Creative Commons Licencia Reconocimiento-No comercial-Compartir bajo la misma licencia 3.0 España de Creative Commons

martes, 18 de diciembre de 2007
Hace unos días Rosario C. realizaba, a través de un comentario en el post "Llamar a métodos estáticos con ASP.Net Ajax", una consulta sobre un problema con el se había topado al intentar retornar DataSets desde un método de página (PageMethod) de ASP.Net Ajax, un tema tan interesante que vale la pena escribir un post en exclusiva.

Recordemos que los métodos estáticos de página son una interesante capacidad que nos ofrece este framework para poder invocar desde cliente (javascript) funciones de servidor (codebehind) de una forma realmente sencilla. Además, gracias a los mecanismos de seriación incluidos, y como ya vimos en el post "Usando ASP.NET AJAX para el intercambio de entidades de datos", es perfectamente posible devolver desde servidor entidades o estructuras de datos complejas, y obtenerlas y procesarlas directamente desde cliente utilizando javascript, y viceversa.

Es es ahí donde reside el problema con los DataSets: precisamente este tipo de datos no está soportado directamente por el seriador JSON incorporado, que es el utilizado por defecto, de ahí que se genere una excepción como la que sigue:

System.InvalidOperationException
A circular reference was detected while serializing an object of type 'System.Globalization.CultureInfo'.
en System.Web.Script.Serialization.JavaScriptSerializer.SerializeValueInternal(Object o, StringBuilder sb, Int32 depth, Hashtable objectsInUse, SerializationFormat serializationFormat)\r\n en System.Web.Script.Serialization.JavaScriptSerializer.SerializeValue(Object o, StringBuilder sb, Int32 depth, Hashtable objectsInUse, SerializationFormat [...]

Pero antes de nada, un inciso. Dado que la excepción se produce en el momento de la seriación, justo antes de enviar los datos de vuelta al cliente, debe ser capturada en cliente vía la función callback especificada como último parámetro en la llamada al método del servidor:

// Llamada a nuestro PageMethod
PageMethods.GetData(param, OnOK, OnError);
[...]


function OnError(msg)
{
// msg es de la clase WebServiceError.
alert("Error: " + msg.get_message());
}
 
Aclarado esto, continuamos con el tema. La buena noticia es que el soporte para DataSets estará incluido en futuras versiones de la plataforma. De hecho, es perfectamente posible utilizar las librerías disponibles en previews disponibles en la actualidad. Existen multitud de ejemplos en la red sobre cómo es posible realizar esto, aunque será necesario instalar alguna CTP y referenciar librerías en el Web.config. Podéis echar un vistazo a lo descrito en este foro.

Esta solución sin embargo no es muy recomendable en estos momentos, puesto que estaríamos introduciendo en un proyecto librerías y componentes que, en primer lugar, están ideados sólo para probar y tomar contacto con las nuevas tecnologías, y, en segundo lugar, las características presentadas en ellos pueden modificarse o incluso eliminarse de las futuras versiones.

Existen otras formas de hacerlo, como la descrita en siderite, que consiste en crear un conversor justo a la medida de nuestras necesidades extendiendo la clase JavaScriptConverter, aunque aún me parecía una salida demasiado compleja al problema.

Sin embargo, la solución que he visto más simple es utilizar XML para la seriación de estas entidades, lo que se puede lograr de una forma muy sencilla añadiendo al método a utilizar un atributo modificando el formato de respuesta utilizado por defecto, de JSON a XML.

He creado un proyecto de demostración para VS2005, esta vez en VB.NET aunque la traducción a C# es directa, con una página llamada DataSetPageMethods.aspx en el que se establece un PageMethod en el lado del servidor, con la siguiente signatura:

<WebMethod()> _
<Script.Services.ScriptMethod(ResponseFormat:=ResponseFormat.Xml)> _
Public Shared Function ObtenerClientes(ByVal ciudad As String) As DataSet
 
Este método estático obtendrá de la tradicional base de datos NorthWind (en italiano, eso sí, no tenía otra a mano O:-)) un DataSet con los clientes asociados a la ciudad cuyo nombre se recibe como parámetro.

Como podréis observar, el método está precedido por la declaración de dos atributos. El primero de ellos, WebMethod(), lo define como un PageMethod y lo hará visible desde cliente de forma directa. El segundo de ellos redefine su mecanismo de seriación por defecto, pasándolo a XML. Si queréis ver el error al que hacía referencia al principio, podéis modificar el valor del ResponseFormat a JSON, su valor por defecto.

Desde cliente, al cargar la página rellenamos un desplegable con las distintas ciudades. Cuando el usuario selecciona un elemento y pulsa el botón "¡Pulsa!", se realizará una llamada asíncrona al PageMethod, según el código:

function llamar()
{
PageMethods.ObtenerClientes($get("<%= DropDownList1.ClientID %>").value , OnOK, OnError);
}
 
La función callback de notificación de finalización de la operación, OnOk, recibirá el DataSet del servidor y mostrará en la página dos resúmenes de los datos obtenidos que describiré a continuación. He querido hacerlo así para demostrar dos posibles alternativas a la hora de procesar desde cliente los datos recibidos, el DataSet, desde servidor.

En el primer ejemplo, se crea en cliente una lista sin orden con los nombres de las personas de contacto de los clientes (valga la redundancia ;-)), donde muestro cómo es posible utilizar el DOM para realizar recorridos sobre el conjunto de datos:

var nds = data.documentElement.getElementsByTagName("NewDataSet");
var rows = nds[0].getElementsByTagName("Table");
var st = rows.length + " filas. Personas:  br />";
st += "<ul>";
for(var i = 0; i < rows.length; i++)
{
st += "<li>" + getText(rows[i].getElementsByTagName("Contatto")[0])+ "</li>";
}
st += "</ul>";
etiqueta.innerHTML = st;
 
En el segundo ejemplo se muestra otra posibilidad, la utilización de DOM para crear una función recursiva que recorra en profundidad la estructura XML mostrando la información contenida. La función javascript que realiza esta tarea se llama procesaXml.

Como se puede comprobar analizando el código, utilizar la información del DataSet en cliente es necesario, en cualquier caso, un conocimiento de la estructura interna de est tipo de datos, lo cual no es sencillo, por lo que recomendaría enviar y gestionar los DataSets en cliente sólo cuando no haya más remedio.

Una alternativa que además de resultar menos pesada en términos de ancho de banda y proceso requerido para su tratamiento es bastante más sencilla de implementar sería enviar a cliente la información incluida un un array, donde cada elemento sería del tipo de datos apropiado (por ejemplo, un array de Personas, Facturas, o lo que sea). La seriación será JSON, mucho más optimizada, además de resultar simplísimo su tratamiento en javascript.

El proyecto de demostración también incluye una página (ArrayPageMethods.aspx) donde está implementado un método que devuelve un array del tipo Datos, cuya representación se lleva también a cliente y hace muy sencillo su uso, como se puede observar en esta versión de la función de retorno OnOk:

function OnOK(datos)
{
var etiqueta = $get("lblMensajes");
var s = "<ul>";
for (var i = 0; i < datos.length; i++)
{
s += "<li>" + datos[i].Contacto;
s += ". Tel: " + datos[i].Telefono;
s += "</li>";
}
s += "</ul>";
etiqueta.innerHTML = s;
}
 

Por último, comentar que el proyecto de ejemplo funciona también con VS2008 (Web Developer Express), aunque hay que abrirlo como una web existente.

Publicado en: www.variablenotfound.com.


Enlace: Descargar proyecto VS2005.

Estos contenidos se publican bajo una licencia de Creative Commons Licencia Reconocimiento-No comercial-Compartir bajo la misma licencia 3.0 España de Creative Commons