Manejando Sys.WebForms.PageRequestManager

Para bien o para mal, el control UpdatePanel es el preferido de la comunidad de ASP.NET AJAX. Y digo “para bien” porque UpdatePanel simplifica de forma espectacular la representación de páginas parciales y “para mal” porque su simplicidad y sencillez de uso se consiguen a costa de la eficacia e, irónicamente, el ancho de banda.

Aunque UpdatePanel proporciona la magia de AJAX a las páginas web normales, no ofrece la eficacia que normalmente se asocia con AJAX. Por ejemplo, ¿sabía que cuando un control UpdatePanel realiza una devolución de llamada asincrónica de AJAX al servidor para actualizar el contenido, la solicitud incluye todo lo que contiene una devolución de ASP.NET convencional, incluido el estado de vista? La mayoría de los desarrolladores supone que AJAX elimina el estado de vista. Esto no es el caso de la marca AJAX de UpdatePanel.

Si va a usar un control UpdatePanel, necesitará saber lo que se que va a encontrar. En muchos casos, desde el punto de vista del rendimiento, es mejor que una aplicación no use UpdatePanel y, en su lugar, emplee llamadas asincrónicas a WebMethods o métodos de páginas. De este modo, puede reducir el volumen de datos que se envía a través de la red en un orden de magnitud o más. Sin embargo, también es un cambio fundamental en el que es necesario que el desarrollador controle explícitamente las actualizaciones de la interfaz de usuario con JavaScript en la página.

Además, los foros de la discusión de ASP.NET AJAX ya están llenos de preguntas sobre la personalización de UpdatePanel. Muchas de estas preguntas se responden fácilmente una vez que se conoce PageRequestManager, la clase de JavaScript de Microsoft® AJAX Library que proporciona compatibilidad de cliente con UpdatePanels.

Ahora que se ha lanzado al mercado ASP.NET AJAX, me gustaría examinar más a fondo UpdatePanel y analizar con más profundidad cómo se puede personalizar, optimizar e incluso vivir sin él. Y eso es exactamente de lo que trata esta columna.

Cómo resaltar las actualizaciones

A veces, sólo se puede sentir lástima por los desarrolladores de Microsoft. Si no hacen bien su trabajo, el público les ataca sin piedad. Sin embargo, a veces también se les critica cuando hacen su trabajo demasiado bien. Por ejemplo, hace poco recibí un correo electrónico de un cliente que se quejaba de que ASP.NET AJAX UpdatePanel funcionaba demasiado bien.

UpdatePanel simplifica mucho la tarea de transformar la intermitencia y el parpadeo que se producen cuando una página de ASP.NET se devuelve al servidor en actualizaciones sencillas y sin parpadeo. UpdatePanel usa su magia para convertir las devoluciones en devoluciones de llamada asincrónicas (solicitudes de HTTP XML) y usar JavaScript en el cliente para actualizar la parte de la página encapsulada por el control UpdatePanel. La intermitencia y el parpadeo desaparecen porque el explorador no vuelve a dibujar la página como sucede durante una devolución.

La queja del cliente era que a veces los usuarios no se daban cuenta de que esa parte de la página se había actualizado con nuevo contenido. Su pregunta era sencilla: ¿los miembros del equipo de ASP.NET AJAX pueden conseguir que UpdatePanel parpadee un poco para que no se dejen pasar actualizaciones importantes?

Lo malo es que probablemente el equipo de ASP.NET AJAX no esté interesado en que UpdatePanel parpadee. A fin de cuentas, deshacerse del parpadeo fue uno de los primeros motivos para crear UpdatePanel. Lo bueno es que puede usar la magia de AJAX en el explorador para llamar la atención sobre las actualizaciones de UpdatePanel. El secreto está en la clase Sys.WebForms.PageRequestManager de Microsoft AJAX Library, la biblioteca de clases de JavaScript que compone la mitad de cliente de ASP.NET AJAX. PageRequestManager administra las devoluciones de llamada asincrónicas iniciadas por UpdatePanel. También es responsable de actualizar el contenido de un UpdatePanel cuando finaliza una devolución de llamada asincrónica.

PageRequestManager desencadena eventos en el explorador antes y después de que se produzca una actualización. Puede enlazar estos eventos en JavaScript y ejecutar código que llame la atención sobre el contenido actualizado. El evento clave se denomina pageLoaded. Este evento se desencadena cada vez que la página se carga en el explorador (de forma análoga a Page_Load en ASP.NET). También se desencadena cada vez que finaliza una devolución de llamada asincrónica iniciada en nombre de un control UpdatePanel y se actualiza el contenido de ese UpdatePanel. Puede registrar un controlador de JavaScript para el evento pageLoaded con dos líneas de código (que se pueden combinar en una):

var prm = Sys.WebForms.PageRequestManager.getInstance();
prm.add_pageLoaded(pageLoaded);

La primera línea establece una referencia al objeto de la página PageRequestManager. La segunda registra una función de JavaScript denominada pageLoaded como controlador de eventos pageLoaded.

Cuando se le llama, un controlador del evento pageLoaded recibe un argumento del tipo Sys.WebForms.PageLoadedEventArgs, que es otra clase de Microsoft AJAX Library. PageLoadedEventArgs contiene un método get_panelsUpdated al que se llama para enumerar todos los UpdatePanels, si hubiera alguno, cuyo contenido se haya actualizado. De forma predeterminada, un UpdatePanel no es nada más que un DIV del cliente, de modo que puede usar JavaScript para generar intermitencia en el DIV, destacarlo o hacer lo que desee para llamar la atención del usuario.

El código enumerado en la figura 1 muestra una de las maneras de usar eventos pageLoaded para resaltar la actualización. Cada vez que tiene lugar una actualización, JavaScript muestra de forma intermitente los elementos del modelo de objetos del documento (DOM) que representan UpdatePanels actualizados, haciéndolos aparecer y desaparecer tres veces consecutivamente. La intermitencia se lleva a cabo mediante una función auxiliar denominada flashPanels, que usa un recuento de intermitencias como parámetro de entrada.

Observe cómo se activa y desactiva la visibilidad de UpdatePanel para crear un efecto de intermitencia. En lugar de interactuar directamente con los elementos DOM, el código ajusta los elementos DOM que representan los UpdatePanels con objetos Sys.UI.Control. A continuación, usa métodos set_visible y get_visible de Sys.UI.Control para mostrar y ocultar un elemento:

_panels[i].set_visible(!_panels[i].get_visible());

Sys.UI.Control es una clase de JavaScript que se encuentra en Microsoft AJAX Library, concretamente en MicrosoftAjax.js. La ventaja de conmutar la visibilidad de este modo es que es independiente del explorador. Esto funciona igual de bien todos los exploradores que admiten ASP.NET AJAX (prácticamente todos los exploradores modernos). Por otro lado, el código JavaScript que interactúa directamente con el explorador DOM se debe ajustar para que funcione con tipos de exploradores diferentes.

Back to top

Cancelación de actualizaciones de UpdatePanel
El evento pageLoaded es uno de los que desencadena la clase PageRequestManager cuando un UpdatePanel regresa al servidor para actualizar su contenido. Otro evento importante que desencadena PageRequestManager es initializeRequest, que tiene lugar antes de que se produzca una devolución de llamada asincrónica.

Alguien me preguntó hace poco si era posible decidir en tiempo de ejecución si se debía permitir que un AsyncPostBackTrigger desencadenara una actualización de UpdatePanel. La respuesta es sí. Esto se lleva a cabo mediante el procesamiento de eventos initializeRequest.

El segundo parámetro que se transmite a un controlador de initializeRequest es un objeto del tipo initializeRequestEventArgs. Este objeto contiene un método get_postBackElement que identifica el botón o el elemento que ha desencadenado la actualización. También dispone de un método set_cancel que se puede usar para cancelar una devolución de llamada antes de que ocurra. Este es un ejemplo del método set_cancel en acción:

<script type=”text/javascript”>

var prm = Sys.WebForms.PageRequestManager.getInstance();
prm.add_initializeRequest(initializeRequest);

function initializeRequest(sender, args)
{
    args.set_cancel(!confirm(‘Are you sure?’));
}
</script>

En este ejemplo, antes de que se ejecute una devolución de llamada, un controlador de intializeRequest abre un cuadro de confirmación que pregunta al usuario si debe continuar la actualización. Al hacer clic en Cancelar en el cuadro de confirmación, se transfiere true a set_cancel, lo que detiene en seco la devolución de llamada. En la vida real, probablemente no le interesa pedir confirmación al usuario antes de permitir que continúe una actualización pero podría ser útil tener la posibilidad de cancelar una actualización en función de las condiciones que existan en otras partes de la aplicación.

Por cierto, también se pueden cancelar las devoluciones de llamada asincrónicas después de que se hayan ejecutado pero antes de que hayan finalizado. PageRequestManager ofrece un método abortPostBack para hacer esto; también proporciona un método get_isInAsyncPostBack para determinar si hay una devolución de llamada asincrónica pendiente. Con frecuencia, estos métodos se usan con controles UpdateProgress para presentar una interfaz de usuario de cancelación.

Back to top

Varios UpdatePanels

Una página puede contener varios UpdatePanels. De forma predeterminada, cuando se actualiza un UpdatePanel en una página, los demás UpdatePanel de la página también se actualizan. A veces esto será lo que quiera pero, pero con frecuencia, no necesitará que se actualicen todos los UpdatePanel como respuesta a la actualización de otros UpdatePanel.

Puede elegir las instancias de UpdatePanel que desee actualizar (y cuándo) si establece en “Condicional” la propiedad de UpdateMode de cada control UpdatePanel de la página. A continuación, cuando se actualice un UpdatePanel y éste llame a un controlador de eventos del cliente, llame a UpdatePanel.Update en los demás paneles que desee actualizar. De este modo se reduce la carga en el servidor, al disminuir el número de controles que se representan, y se reduce el volumen de datos de la respuesta porque los UpdatePanel que no se actualicen no agregarán nada a la respuesta.

Back to top

Actualizaciones sin UpdatePanel

AJAX no sólo se ocupa de mejorar la experiencia del usuario; también trata de ofrecer mayor eficacia a la transmisión. Cuando se produce una devolución tradicional de ASP.NET, todos los datos del formulario web, incluido el estado de vista, se desplazan al servidor en la devolución. El estado de vista es uno de los motivos por los que las páginas de ASP.NET, sobre todo las que usan controles DataGrid y GridView, pueden parecer lentas. Las páginas con una gran cantidad de estado de vista son un problema para el rendimiento y este tipo de páginas es muy frecuente en las aplicaciones de ASP.NET.

Una de las ventajas de sustituir las devoluciones de ASP.NET por devoluciones de llamada de AJAX es que, si se realizan correctamente, las devoluciones de llamada de AJAX sólo transmiten los datos que es necesario transmitir. Esto significa que no tienen que incluir el estado de vista en la transmisión.

Cuando emplea UpdatePanel para realizar actualizaciones sin parpadeo en una página, es posible que piense que está aumentando la eficacia. A fin de cuentas, UpdatePanel usa AJAX, ¿no es así? Lamentablemente, si comprueba el tráfico de transmisión cuando se actualiza un UpdatePanel, descubrirá que no está ahorrando mucho, por lo menos en el envío. Los datos del estado de vista (y otros datos) que normalmente se transmiten al servidor durante una devolución, también se transmiten durante las devoluciones de llamada de UpdatePanel. En realidad, lo que aumenta en una solicitud asincrónica de HTTP XML de un UpdatePanel es casi idéntico a lo que aumenta en una devolución estándar de ASP .NET. Éste es el pequeño secreto de ASP.NET AJAX: UpdatePanel ofrece sencillez de uso pero no eficacia en la transmisión.

Puede hacer muy poco para aumentar la eficacia de UpdatePanel pero puede renunciar el uso de UpdatePanel y usar otras características de ASP.NET AJAX para actualizar el contenido de las páginas de un modo igual de sencillo pero mucho más eficaz. Se tarda un poco más pero los resultados merecen la pena, ya que se puede reducir mucho el volumen de datos que se desplaza entre el cliente y el servidor.

También se puede reducir la carga del servidor. Cuando un UpdatePanel devuelve una llamada al servidor, la página a la que se dirige la devolución recorre un ciclo de vida casi completo; se crea una instancia de la página y de los controles de la página, y los controles de UpdatePanel experimentan un ciclo de representación normal. Esto supone una gran sobrecarga sólo para actualizar una parte de la página.

Por ejemplo, piense en el fragmento de página de la figura 2. Ofrece una interfaz de usuario sencilla que permite que el usuario escriba un código postal y haga clic en un botón para inicializar los campos de ciudad y estado/provincia con una ciudad y un estado/provincia (consulte la figura 3). Todos los controles se alojan en un UpdatePanel, de modo que la devolución del control de botón se convierte en una devolución de llamada asincrónica y se llama al controlador de eventos (GetCityAndState) en el servidor, dentro de la devolución de llamada. GetCityAndState (el código no aparece) lee el código postal del TextBox ZipCode, lo convierte en una ciudad y un estado/provincia e inicializa el TextBox y la DropDownList que representan la ciudad y el estado/provincia. Dado que todo esto ocurre dentro de un UpdatePanel, la actualización es perfecta y sin parpadeo.

Figura 3 Interfaz de usuario de ciudad, estado/provincia y código postal

Éste es el problema. Si se usa un UpdatePanel de este modo, mejora la experiencia del usuario, pero se reduce poco el volumen de datos que se transmite. El UpdatePanel apenas reduce la carga en el servidor; es decir, hasta el punto en que los controles que se encuentran dentro del UpdatePanel se representan, el procesamiento que tiene lugar en el servidor es casi idéntico al que se produce durante una devolución completa. Es así porque una de las ventajas del control UpdatePanel es que los controladores de eventos del servidor, tal como GetCityAndState, no funcionan de forma diferente en una devolución de llamada asíncrona y en una devolución tradicional. Esto significa que se debe crear una instancia de los controles de la página, se deben inicializar, deben tener acceso al estado de vista, etc.

En la figura 4 se demuestra el modo de implementar la misma funcionalidad sin un control UpdatePanel. En esta ocasión, el botón Autofill está conectado a una pequeña parte de JavaScript que desencadena una solicitud de HTTP XML asincrónico a un método web de ASMX denominando GetCityAndState. La llamada se realiza a través del proxy de JavaScript denominado ZipCodeService, que se genera a través de la referencia de servicio en el control de ScriptManager. GetCityAndState toma una cadena de código postal como entrada y devuelve una matriz de cadenas que contiene dos elementos: la ciudad y el estado/provincia correspondientes. La función de finalización onGetCityAndStateCompleted recupera los elementos de la matriz y los inserta en los campos de ciudad y estado/provincia. Aparentemente tiene el mismo resultado, pero el modo de llegar a él es muy distinto en el interior.

De este modo se implementa la llamada al método web de ASMX a través del proxy de JavaScript:

[ScriptService]
public class ZipCodeService : System.Web.Services.WebService
{
    [WebMethod]
    public string[] GetCityAndState(string zip)
    {
      …
    }
}

Se trata de un método web estándar en todos los aspectos, pero la clase a la que pertenece tiene el atributo ScriptService en lugar de WebService. ScriptService es sinónimo de WebService, pero con el significado añadido de que se puede llamar a los WebMethods del servicio web desde la secuencia de comandos del lado cliente.

Además de permitir que los WebMethods convencionales actúen como destino de las solicitudes de HTTP XML, ASP.NET AJAX también admite un tipo especial de método web conocido como el método de página. Los métodos de página son WebMethods que se implementan en páginas web; es decir, en archivos ASPX o archivos de código subyacente en lugar de hacerlo en archivos ASMX. Los métodos de página permiten a los desarrolladores ofrecer extremos para devoluciones de llamadas XML-HTTP sin necesidad de crear servicios web dedicados.

Los métodos de página deben ser métodos estáticos públicos y, al igual que los WebMethods, llevar el atributo WebMethod. (Los métodos WebMethods y de página también pueden llevar el atributo ScriptMethod, que ofrece un mayor control sobre lo que se transmite.) En el cliente, se llama a los métodos de página desde JavaScript a través del proxy especial PageMethods.

A diferencia de servicios web, los métodos de página no necesitan referencias de servicio. Sin embargo, tiene que habilitar métodos de página mediante el establecimiento en true de la propiedad EnablePageMethods del control ScriptManager, tal como se muestra a continuación:

<asp:ScriptManager ID=”ScriptManager1” runat=”server”
  EnablePageMethods=”true” />

En realidad, los métodos de página ofrecen la misma eficacia que los WebMethods. El estado de vista y otras entradas no se transmiten al servidor cuando se llama a los métodos de página. Y, dado que los métodos de página son estáticos, se les puede llamar sin crear una instancia de un objeto de página. Al llamar a un método de página, no se invoca el ciclo de vida de la página que se activa mediante solicitudes de ASP.NET convencionales.

Back to top

Servicio web != SOAP y XML

Una de las características más importantes de ASP.NET AJAX es su capacidad para invocar métodos WebMethods y de página en un servidor usando solicitudes asincrónicas de HTTP XML desde clientes de explorador. No obstante, siempre que cuento esta característica a la gente, no puedo dejar de avergonzarme un poco.

La mayoría de nosotros, cuando escuchamos el término servicio web, pensamos en SOAP y XML. Ninguna de estas tecnologías se suele mencionar cuando se habla de eficacia. Sí, puede llamar a WebMethods desde JavaScript usando ASP.NET AJAX. Pero no, ASP.NET AJAX no usa ni SOAP ni XML.

En la figura 5 se muestra lo que se desplaza en la transmisión cuando se ejecuta la llamada al método web de la figura 4. Además de los encabezados HTTP, los únicos datos que se transmiten en la solicitud son los del código postal que escribe el usuario, y los únicos datos que se devuelven en la respuesta son los de un par de cadenas que representan la ciudad y el estado/provincia. No verá ningún SOAP ni XML (ni el estado de vista correspondiente). En su lugar, la entrada y la salida se codifican usando JavaScript Object Notation (JSON), que es mucho menos detallado que XML y más fácil de procesar. En lugar de SOAP, la solicitud y la respuesta usan un protocolo sencillo que es básicamente HTTP. La combinación de HTTP y JSON permite que las llamadas de ASP.NET AJAX a WebMethods y los métodos de página sean mucho más eficaces que las llamadas tradicionales a servicios web.

JSON es un formato de serialización estándar del sector muy prometedor. Además, es el formato nativo que emplea ASP.NET AJAX. En el cliente, la compatibilidad con la serialización y deserialización se proporciona a través de la clase Sys.Serialization.JavaScriptSerializer de Microsoft AJAX Library. En el servidor, la compatibilidad se proporciona a través de la clase System.Web.Script.Serialization.JavaScriptSerializer.

No todos los tipos son compatibles con JSON. Por ejemplo, JSON no puede administrar objetos con referencias circulares. Si necesita devolver tipos de datos complejos que no sean compatibles con JSON, puede usar el atributo ScriptMethod de ASP.NET AJAX para serializar los tipos de devolución en XML. Esta técnica también es útil para los métodos que devuelven datos XML, tal como se demuestra a continuación:

[ScriptMethod (ResponseFormat=ResponseFormat.Xml)]
public XmlDocument GetData()
{
  …
}

De manera alternativa, puede crear y registrar convertidores personalizados de JSON que permiten que los tipos que normalmente no son compatibles con JSON se serialicen y deserialicen. ASP.NET AJAX Futures CTP de enero contiene estos tres convertidores: uno para Dataset, otro para DataTable y un tercero para DataRow.

Anuncios

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s

A %d blogueros les gusta esto: