El Blog de Óscar Sotorrío Sánchez - MCP Application Development Foundation - C#

Todo lo que vaya aprendiendo de .NET, C#, ASP.NET, SQL Server, etc, lo compartiré con vosotros.

El autor:

Óscar Sotorrío Sánchez
Contacto Send mail

Encuéntralo

Álguien dijo...


Una cara mentirosa debe ocultar lo que sabe un corazón falso.

William Shakespeare

Peleándome con el UpdatePanel

Cuando empezamos a trabajar con los controles ScriptManager y UpdatePanel normalmente nos quedamos encantados de lo rápido, sencillo y bonito que es todo. Pero cuando estamos en el trabajo,en aplicaciones reales, cuando se nos pide implementar cierta funcionalidad, nos damos cuenta que todo este automatismo tan bonito es poco elástico. Y que para conseguir la funcionalidad requerida por el cliente o por tu jefe, no nos queda más remedio que...¡pelearnos con el UpdatePanel!.

Vamos a ver un ejemplo de esta "pelea". Y para entrar en harina nada mejor que ir paso a paso chocando con todos lo problemas. Os recomiendo que copies el código en vuestro Visual Studio y ejecutéis todos los pasos que aquí se explican para daros de morros con todos lo problemas que surjan.

Supongamos que tenemos que implementar la siguiente funcionalidad.

  1. Mostrar un informe con los 4 primeros clientes de la tabla "Customers" de la conocida base de datos Northwind.
  2. Solo mostraremos los datos de los campos siguientes: CompanyName, ContactName, Address y Phone.
  3. En cada fila o registro del informe, debe haber un botón que muestre la fila selecionada escribiendo en el objeto HttpResponse. Es decir, que cada botón de cada fila debe hacer un PostBack para mostrar la fila seleccionada. Atentos porque esta es la verdadera dificultad.
  4. Otro botón, externo al informe, debe permitir mostrar los 10 primeros clientes y esta operación debe hacerse con AJAX.
Ahora nosotros como desarrolladores nos ponemos a pensar..¡que peligro!.
 
Y pensando, y pensando, llegamos a la conclusión de vamos a utilizar un control DataList para mostrar el informe. Y que este control lo meteremos dentro de un UpdatePanel registrando el botón externo al informe como disparador asíncrono para mostrar los 10 primeros resultados por AJAX. Y también registraremos el botón de cada fila del informe como disparador síncrono para que muestre la fila seleccionada con un PostBack.
 
Ya lo tenemos todo claro, y estamos seguros de que esto es muy fácil. ¡Veamos que pasa.!
 
            <asp:Button ID="Button1" runat="server" Text="Mostrar 10 resultados" onclick="Button1_Click" />
            <
asp:DataList ID="DataList1" runat="server" DataSourceID="SqlDataSource1" 
                onitemcommand
="DataList1_ItemCommand">
                
<ItemTemplate>
                    
<br />
                    <
br />
                    CompanyName:
                    <
asp:Label ID="CompanyNameLabel" runat="server" 
                        Text
='<%# Eval("CompanyName") %>' />
                    <
br />
                    ContactName:
                    <
asp:Label ID="ContactNameLabel" runat="server" 
                        Text
='<%# Eval("ContactName") %>' />
                    <
br />
                    Address:
                    <
asp:Label ID="AddressLabel" runat="server" Text='<%# Eval("Address") %>' />
                    <
br />
                    Phone:
                    <
asp:Label ID="PhoneLabel" runat="server" Text='<%# Eval("Phone") %>' />
                    <
br />
                    <
asp:Button ID="Button2" runat="server" Text="Mostrar Fila" />
                </
ItemTemplate>
            
</asp:DataList>
            
<asp:SqlDataSource ID="SqlDataSource1" runat="server" 
                ConnectionString
="<%$ ConnectionStrings:NorthwindConnectionString %>" 
                SelectCommand
="SELECT TOP 4 [CompanyName], [ContactName], [Address], [Phone] FROM [Customers]">
            
</asp:SqlDataSource>
 
Añadimos este código a nuestro formulario. En primer lugar definimos el botón que mostrará los 10 primeros registros de la tabla "Customers". Después definimos el DataList que al cargarse la página gracias al SqlDataSource mostrará los 4 primeros resultados. Como vemos, en cada item o fila hemos definido el botón que mostrará la fila seleccionada. Solo nos falta escribir el código de cada botón.
 
    protected void DataList1_ItemCommand(object source, DataListCommandEventArgs e)
    {
        Response.Write(
"Fila seleccionada = " + (e.Item.ItemIndex + 1));
    
}

    
protected void Button1_Click(object sender, EventArgs e)
    {
        SqlDataSource1.SelectCommand 
"SELECT TOP 10 [CompanyName], [ContactName], [Address], [Phone] FROM [Customers]";
    
}
 
Podemos ejecutar la aplicación para comprobar que todo funciona perfectamente. Solo hay un problema, que cuando añadimos los 10 primeros resultados no lo hacemos por AJAX como se nos ha especificado. Así que manos a la obra.
 
Incluimos un control ScriptManager y un UpdatePanel que contenga el control DataList que muestra el informe. Y definimos los disparadores de control UpdatePanel...
 
                <Triggers>
                    
<asp:AsyncPostBackTrigger ControlID="Button1" EventName="Click" />
                    <
asp:PostBackTrigger ControlID="Button2" />
                </
Triggers>
  
Como vemos hemos registrado el control Button1 para que por AJAX muestre los 10 primeros registros. Y también hemos registrado el Button2 para muestre la fila seleccionada pero sin que se ejecute con AJAX. El código de nuestro formulario quedaría de la siguiente forma:
 
    <asp:ScriptManager ID="ScriptManager1" runat="server"></asp:ScriptManager>
        
<div>
            
<asp:Button ID="Button1" runat="server" Text="Mostrar 10 resultados" onclick="Button1_Click" />
            <
asp:UpdatePanel ID="UpdatePanel1" runat="server">
                
<ContentTemplate>
                    
<asp:DataList ID="DataList1" runat="server" DataSourceID="SqlDataSource1" 
                        onitemcommand
="DataList1_ItemCommand">
                        
<ItemTemplate>
                            
<br />
                            <
br />
                            CompanyName:
                            <
asp:Label ID="CompanyNameLabel" runat="server" 
                                Text
='<%# Eval("CompanyName") %>' />
                            <
br />
                            ContactName:
                            <
asp:Label ID="ContactNameLabel" runat="server" 
                                Text
='<%# Eval("ContactName") %>' />
                            <
br />
                            Address:
                            <
asp:Label ID="AddressLabel" runat="server" Text='<%# Eval("Address") %>' />
                            <
br />
                            Phone:
                            <
asp:Label ID="PhoneLabel" runat="server" Text='<%# Eval("Phone") %>' />
                            <
br />
                            <
asp:Button ID="Button2" runat="server" Text="Mostrar Fila" />
                        </
ItemTemplate>
                    
</asp:DataList>
                
</ContentTemplate>
                
<Triggers>
                    
<asp:AsyncPostBackTrigger ControlID="Button1" EventName="Click" />
                    <
asp:PostBackTrigger ControlID="Button2" />
                </
Triggers>
            
</asp:UpdatePanel>
            
<asp:SqlDataSource ID="SqlDataSource1" runat="server" 
                ConnectionString
="<%$ ConnectionStrings:NorthwindConnectionString %>" 
                SelectCommand
="SELECT TOP 4 [CompanyName], [ContactName], [Address], [Phone] FROM [Customers]">
            
</asp:SqlDataSource>
    
</div>
 
 
Ejecutemos la aplicación para ver que sucede.
 
 
 
 
Recibimos el error: No se pudo encontrar un control con el id. 'Button2' para el desencadenador de UpdatePanel 'UpdatePanel1'. 
 
 
¿Pero porque?. En realidad es lógico, el botón de cada fila se va a generar dinámicamente. Se crearán tantos botones como registros o filas mostremos en el control DataList. Y naturalmente cada botón tendrá un Id de cliente diferente, por lo tanto el UpdatePanel no tiene ni idea de a que disparador nos referimos cuando le decimos que registre el Button2, porque no existe un solo botón.
 
 
Eliminamos de nuestro código el registro de este botón maldito. Es decir, quitamos lo siguiente y ejecutamos la aplicación.
 
            <asp:PostBackTrigger ControlID="Button2" /> 
 
 
Parece que todo funciona correctamente. Pulsamos el botón para mostrar los 10 primeros registros y todo va como la seda. Pero que ocurre si pulsamos un botón de una de las filas.
 
 
 
 
¿Pero que es esto?. Al pulsar el botón estamos intentado escribir en el objeto HttpResponse de nuestra página. Y como estamos dentro del esquema de trabajo del UpdatePanel nuestra página ya no es la encargada de devolver el HTML al cliente. Ahora toman el mando de la respuesta los controles ScriptManager y UpdatePanel. 
 
 
¿Y como narices solucionamos esto?. Ejecutemos otra vez la aplicación pero no hagamos nada. Simplemente veamos el código fuente que se genera la primera vez que se carga la página.
 
    <script type="text/javascript">
    
//<![CDATA[
    
Sys.WebForms.PageRequestManager._initialize('ScriptManager1'document.getElementById('form1'));
    
Sys.WebForms.PageRequestManager.getInstance()._updateControls(['tUpdatePanel1'], ['Button1'], [], 90);
    
//]]>
    
</script>
 
Al principio del código fuente nos encontramos con este script autogenerado. ¿Y que hace este script?. 
 
En la primera linea instancia la clase cliente PageRequestManager y registra nuestro ScriptManager en nuestro formulario. Pero fijaros en la segunda linea. Fijaros que el botón que hemos definido para que muestre los 10 primeros resultados esta entre corchetes. Y fijaros también en que hay otros corchetes que no contienen nada.
 
Pues bien, esta linea lo que hace es registrar en el UpdatePanel cuales serán los controles disparadores que provocarán la actualización parcial del contenido del UpdatePanel. El primer corchete es para los controles que provocarán una llamada AJAX, y el segundo, como es lógico, serán los controles que provocarán un PostBack en toda regla.
 
He hablado de controles en plural porque entre estos corchetes podemos definir la colección de todos los disparadores del UpdatePanel. 
 
    Sys.WebForms.PageRequestManager.getInstance()._updateControls(['tUpdatePanel1'], ['btAjax1','btAjax2',...],
 ['btPostBack1','btPostBack2',...], 90); 
 
 
Ahora que sabemos que este script es el encargado de registrar los disparadores para el UpdatePanel es muy fácil imaginar que también podremos hacerlo nosotros desde el código cliente. ¡Veamos como!.
 
 
Creamos nuestro propio script que registre solamente los botones que se generan dinámicamente, es decir, cada botón de cada fila. Y lo vamos hacer bajo demanda del usuario. Este sería el script que habría que escribir justo debajo de la definición de nuestro ScriptManager.
  
    <asp:ScriptManager ID="ScriptManager1" runat="server"></asp:ScriptManager>
    <script type
="text/javascript" language="javascript">
        
function RegistrarTriggers(control) {
            
Sys.WebForms.PageRequestManager.getInstance()._updateControls(['tUpdatePanel1'], [], [control.id], 90);
        
}
    </script>
Ahora solo nos queda colocar la llamada a esta función en el botón que se generará para cada fila del informe.
 
<asp:Button ID="Button1" OnClientClick="RegistrarTriggers(this)" runat="server" Text="Mostrar Fila" />
 
¡Y ya esta todo!. Comprobarlo ejecutando la aplicación y veréis como cada botón de las filas del informe muestra la fila seleccionada escribiendo en el objeto HttpResponse provocando un PostBack.
 
Quizás alguien se ha preguntado porque este ejemplo tan raro. Es una simplificación de una aplicación real, en la que se pretendía mostrar un informe más complejo en el que cada fila tuviera un botón que exportara a Excel el contenido de la fila pulsada. Este Excel, se enviaba desde el servidor en la cabecera del objeto HttpResponse. Por este motivo, los botones de cada fila deberían generar un PostBack y permitir escribir en el objeto HttpResponse de la página.
 
 
Agradecimientos: Este artículo no podría haberse escrito sin la colaboración de un buen compañero. ¡Gracias Carlos!. 
 
 

Tags:
Categories: Desarrollo Web
Posted by Oscar.SS on sábado, 06 de marzo de 2010 11:57
Permalink | Comentarios (0) | Post RSSRSS comment feed

Eventos del ciclo de vida cliente en ASP.NET AJAX

Como ya hemos comentado alguna vez, ASP.NET con AJAX provoca en el servidor los mismo eventos que durante un PostBack en el ciclo de vida de una página. Pero además, cuando tenemos habilitado AJAX en nuestra página, provoca eventos en el ciclo de vida cliente. El conocer estos eventos y su ciclo es muy importante para tener el mayor control y manejo posible de las solicitudes asíncronas.
 
Estos eventos en el cliente son provocados por las clases de la librería de AJAX .NET. Esta librería y las instancias de sus clases están a nuestra disposición automáticamente al incluir un control ScriptManager al inicio de nuestras páginas.
 
Son dos las clases principales que provocan estos eventos durante el ciclo de vida de la página en el cliente. Tenemos la clase Sys.Application que para entendernos es similar a la clase Page del objeto de servidor. Y tenemos la clase Sys.WebForms.PageRequestManager.
 
Veamos por partes como ocurre todo tanto durante un petición síncrona (PostBack), como una petición asíncrona (AJAX).
 
 
Ciclo de eventos cliente durante un PostBack
 
Cuando arrancamos por primera vez nuestra aplicación, o cuando hacemos un viaje al servidor de nuevo, el ciclo de eventos que se provocan en el cliente son los siguientes.
  • Window.load(). Este evento del DOM como ya sabemos se dispara cuando se carga nuestra página.
  • PageRequestManager.pageLoaded(). Este evento se dispara tanto durante una llamada asíncrona como una síncrona cuando se ha actualizado todo el contenido de la página. Es el evento ideal para determinar en el código cliente si la devolución de datos fué realizada por AJAX o por un PostBack.
  • Application.init(). Se dispara cuando se terminan de cargar todos los script de cliente pero antes de que se cree ningún objeto de estos. Es decir, que si intentamos acceder durante este evento a cualquiera de los objetos declarados en nuestros script recibiremos un error. Otra cosa muy importante a tener en cuenta en este evento es que se dispara una única vez por cada devolución de datos síncrona, nunca durante una devolución asíncrona.
  • Application.load(). Este evento se dispara cuando se han cargado todos los objetos de los script de la página.
  • Application.unload(). Entra en acción antes de eliminar todos los objetos script de la página.
  • Window.unload(). Este evento del DOM como se dispara cuando se cierra nuestra ventana del explorador.

 
Ciclo de eventos cliente durante AJAX en .NET
 
Ahora viene lo que realmente debemos tener en cuenta. No olvidemos que estamos trabajando bajo el esquema de un control ScriptManager y un control UpdatePanel en nuestra página. Esto significa que cuando el servidor nos devuelve los datos, se realiza una actualización parcial de la página, en concreto, solo obtenemos el HTML necesario para repintar el contenido de los elementos del interior del UpdatePanel.

  • PageRequestManager.initializeRequest(). Este es el primero de los eventos que se disparan cuando provocamos una llamada AJAX. En realidad, durante este evento aún no ha comenzado la solicitud, por lo tanto, es el evento ideal para cancelar la llamada desde nuestro código cliente.
  • PageRequestManager.beginRequest(). Se provoca antes de que se envíen los datos al servidor. Es el último evento que se produce en el cliente. Es el momento ideal para llamar a un script personal que inicie una animación que notifique al usuario que se está procesando la devolución de datos. El control UpdateProgress hace uso de este evento para mostrar su contenido.
  • PageRequestManager.pageLoading(). Se dispara después de recibir la respuesta del servidor durante una llamada AJAX, pero antes de actualizar cualquier contenido en la página. Es una oportunidad perfecta para realizar cualquier acción personalizada, antes de ser pintados, sobre los datos recibidos desde el servidor, como filtrarlos o formatearlos. También es el evento usado para enviar datos a los controles que no están incluidos en el updatepanel.
  • PageRequestManager.pageLoaded(). Este evento se dispara tanto durante una llamada asíncrona como una síncrona cuando se ha actualizado todo el contenido de la página. Es el evento ideal para determinar en el código cliente si la devolución de datos fué realizada por AJAX o por un PostBack.
  • Application.load(). Este evento se dispara cuando se han cargado todos los objetos de los script de la página.
  • PageRequestManager.endRequest(). Se dispara después del repintado parcial de la página y se devuelve el control al explorador. También se dispara este evento cuando se ha producido un error durante la devolución de datos asíncrona. Si se produce un error, la página no se actualiza por lo tanto los datos devueltos por el servidor no son mostrados. Se puede utilizar este evento para proporcionar una notificación de error personalizada a los usuarios o para registrar los errores. También se utilizar para ejecutar script personalizados al terminar la devolución de datos asíncrona.
 
 
Pues muy bien, mucho rollo pero...¿como se declaran estos eventos en el cliente?. Pongamos todo esto en práctica.
<script type="text/javascript" language="javascript">

        
//Eventos del objeto Application

        
Sys.Application.add_init(applicationInitHandler);

        function 
applicationInitHandler() {
        
//Código para este evento
        
}


        
//Eventos del objeto PageRequestManager

        
Sys.WebForms.PageRequestManager.getInstance().add_initializeRequest(prmInitializeRequest);

        function 
prmInitializeRequest() {
        
//Código para este evento
        
}
 </script>
La clase Sys.Application se instacia directamente, no así la clase Sys.WebForms.PageRequestManager que para intstanciarla debemos utilizar el método getInstance().
 
Los eventos de ambas clases se declaran al estilo de los delegados de C#, y siempre siguen la misma sintaxis: add_nombreEvento (nombreFuncion)
 
Podéis descargaros un pequeño ejemplo para jugar con todo esto: EventosClienteAJAX.rar (3,52 kb)
 
 

Posted by Oscar.SS on domingo, 21 de febrero de 2010 19:53
Permalink | Comentarios (2) | Post RSSRSS comment feed

Instalar la libería de AJAX Control Toolkit

La biblioteca de controles AJAX Control Toolkit es una poderosa característica que debemos tener siempre presente. Por ello vamos a ver como se instala en 7 sencillos pasos, para que quede integrada en nuestra herramienta de desarrollo Visual Studio 2008.

1. En primer lugar debemos dirigirnos al sitio oficial de descarga, que no es otro que CodePlex. En el siguiente enlace buscaremos la zona de descarga.

http://ajaxcontroltoolkit.codeplex.com/

 

2. Elegimos la versión que queremos descargar y descomprimimos el zip. En el momento de escribir este artículo esta disponible la versión que se muestra en la imagen.

 

3. Nos dirigimos a nuestro Visual Studio y abrimos un editor de páginas aspx. En el cuadro de herramientas, en la última ficha, "General", con el botón secundario seleccionamos la opción "Agregar Ficha". Le damos un nombre a la nueva ficha, por ejemplo, AJAX Control Toolkit.

 

4. Sobre la superficie de la nueva ficha repetimos la operación y esta vez seleccionamos "Elegir Elementos".

 

5. En la ventana que se abre, elementos del cuadro de herramientas, presionamos el botón "Examinar".

 

6. Buscamos la carpeta que acabamos de descargar y dentro de ella seleccionamos la siguiente dll. 

........./SampleWebSite/Bin/AjaxControlToolkit.dll

 

7. Presionamos el botón "Aceptar" y esperamos a que terminen de cargarse todos los binarios. Y ya tenemos todos los controles de AJAX en el cuadro de herramientas listos para ser utilizados.

¡A disfrutarlo! 


Posted by Oscar.SS on domingo, 07 de febrero de 2010 18:21
Permalink | Comentarios (0) | Post RSSRSS comment feed

AJAX en ASP.NET 2.0 con Web Services

Para terminar con esta serie ( IIIIIIIV y ahora V) de las posibilidades que nos ofrece ASP.NET y AJAX, vamos a ver como realizar una llamada asíncrona al servidor a través de un servicio web.

Conceptualmente no se diferencia en nada de otras versiones de ASP.NET. Configurando convenientemente el servicio web podremos llamar a este desde un script de cliente y todo de forma asíncrona. Con ASP.NET 2.0, como siempre, nos ayudará en esta tarea el control ScriptManager.

 

 
 
 
Código Cliente
 
En primer lugar debemos registrar por medio del ScriptManager el servicio web y también como ya es costumbre el script donde se encuentra el código que realiza la llamada.
    <asp:ScriptManager ID="ScriptManager1" runat="server">
        
<Services>
            
<asp:ServiceReference Path="ServicioWeb.asmx" />
        </
Services>
        
<Scripts>
            
<asp:ScriptReference Path="funcionesWebService.js" />
        </
Scripts>
    
</asp:ScriptManager>

En el archivo funcionesWebService.js como ya hemos comentado se encuentra el código que envía los datos al servidor y también los recoge y manipula. A estas alturas este código, con tres funciones, una para enviar datos, otra para recogelos y la última en caso de error durante el proceso asíncrono, es de sobra conocido por todos.

//Funcion que llama al servicio web de forma asíncrona.
function EnviarDatos() {

    
//$get('') == document.getElementById('')
    
var nombreSeleccionado $get("listaNombres").value;

    
//Llamada al servicio web.
    
ServicioWeb.ObtenerDatos(nombreSeleccionado, RecogerRespuesta, RecogerErrores)
}


//Funcion que recoge los datos desde el servidor.
function RecogerRespuesta(datosRecibidos) {

    $
get("txRespuesta").innerText datosRecibidos;
}


//Función que recoge los posibles errores.
function RecogerErrores(error) {

    
alert("Error: " + error.get_message());
}

 

Código Servidor

En el archivo de código de nuestro servicio web debemos implementar el método que recibirá la petición del cliente y después de hacer las operaciones oportunas devolverá los datos al script de cliente.

[System.Web.Script.Services.ScriptService]
public class ServicioWeb : System.Web.Services.WebService 
{

    
public ServicioWeb () 
    {}


    [WebMethod]
    
public string ObtenerDatos(string nombreSelect) {

        
string ruta Server.MapPath("~/App_Data/datos.xml");

        
//Antes de enviar la petición simulamos una tarea larga.
        
System.Threading.Thread.Sleep(5000);

        return 
ManejarDatos.ObtenerCategoria(nombreSelect, ruta);
    
}
    
}

Como vemos hemos decorado la clase del servicio web con el atributo System.Web.Script.Services.ScriptService, de esta forma indicamos que la clase contiene servicios que serán utilizados mediante script de cliente. También hemos, al igual que con los Page Methods, decorado el método que recibe la llamada AJAX con el atributo System.Web.Services.WebMethod para indicar que se pretende exponer este métedo como parte del servicio web.

 

Código de descarga 

El código completo mencionado en este artículo se encuentra en la carpeta AJAX_NET_3.5/Retro_Llamadas/AJAX_WebService del siquiente enlace. Para probar la aplicación ejecutar la página Default.aspx de la carpeta mencionada.

 

AJAX_ASP.NET.rar (15,45 kb)


Tags:
Categories: Desarrollo Web
Posted by Oscar.SS on domingo, 10 de enero de 2010 15:08
Permalink | Comentarios (0) | Post RSSRSS comment feed

AJAX en ASP.NET 2.0 con Page Methods

En el artículo anterior de esta serie vimos la potencia y facilitdad que nos ofrece un control UpdatePanel combinado con un ScriptManager. Pero no es oro todo lo que reluce. ¿Que sucede si tenemos una página con muchos controles pero solo queremos actualizar por AJAX uno de ellos?.

Tenemos que tener en cuenta que cuando utilizamos un UpdatePanel estamos enviando al servidor todo el HTML de la página. Es decir, no solo estamos enviando el contenido del UpdatePanel, enviamos también todos los controles que encuentran fuera. Y esto aumenta mucho el tráfico hacia el servidor. Además, en el servidor se disparan todos los eventos del ciclo de vida de la página y de todos los controles que contenga. Lo que recarga las funciones realizadas por el servidor.

Eso si, el servidor gracias al UpdatePanel y a las librerías cliente que nos ofrece el ScriptManager, solo envía el HTML necesario para actualizar los controles que se encuentren dentro del UpdatePanel.

Estas consideraciones debemos tenerlas en cuenta si estamos implementado AJAX en una página muy pesada con mucho controles. La implementación de los métodos de página o Page Methods nos ofrece la posibilidad de tener más control sobre la forma de actualizar la página a cambio de perder un poco de automatización.

 

 

 

Código Cliente

Lo primero que tenemos que hacer es habilitar la funcionalidad de los métodos de página en el control ScriptManager por medio de la propiedad EnablePageMethods.

    <asp:ScriptManager ID="ScriptManager1" runat="server" EnablePageMethods="true">
        
<Scripts>
            
<asp:ScriptReference Path="funcionesPageMethods.js" />
        </
Scripts>
    
</asp:ScriptManager> 

Por otro lado, hemos añadido una referencia al archivo JavaScript que contendrá las funciones necesarias para el envío y recogida de datos de forma asíncrona. Este es el código.

//Funcion que llama al método de la página de forma asíncrona.
function EnviarDatos() {

    
//$get('') == document.getElementById('')
    
var nombreSeleccionado $get("listaNombres").value;
    
    
//Llamada al metodo de la página.
    
PageMethods.ObtenerDatos(nombreSeleccionado, RecogerRespuesta, RecogerErrores);
}


//Funcion que recoge los datos desde el servidor.
function RecogerRespuesta(datosRecibidos)
{
    $
get("txRespuesta").innerText datosRecibidos;
}


//Función que recoge los posibles errores.
function RecogerErrores(error) {

    
alert("Error: " + error.get_message());
}

Ya estamos acostumbrados a este tipo de implementación, una función para enviar los datos asíncronos, otra para recogerlos y una función para recoger los errores que se produzcan durante la llamada. Lo único que merece la pena resaltar es esta linea.

 PageMethods.ObtenerDatos(nombreSeleccionado, RecogerRespuesta, RecogerErrores);

 

La clase PageMethods intrínseca de la librería ASP.NET AJAX nos permite llamar a la función ObetenerDatos() que es un método estático que se encuentra en nuestra página.

 

Código Servidor

En el código de servidor solo tendremos que implementar el método que se encarga de recoger los datos desde el cliente y enviarlos asíncronamente una vez manipulados de vuelta al cliente. 

    [System.Web.Services.WebMethod]
    
public static string ObtenerDatos(string nombreSelec)
    {
        
//Antes de enviar la petición simulamos una tarea larga.
        
System.Threading.Thread.Sleep(5000);
        
        return 
ManejarDatos.ObtenerCategoria(nombreSelec, rutaDatos);
    
}

Como vemos este método tiene que ser static y estar decorado con el atributo System.Web.Services.WebMethod.

 

Código de descarga 

El código completo mencionado en este artículo se encuentra en la carpeta AJAX_NET_3.5/Retro_Llamdas/AJAX_PageMethods del siquiente enlace. Para probar la aplicación ejecutar la página AJAX_PageMethods.aspx de la carpeta mencionada.

 

AJAX_ASP.NET.rar (15,45 kb)


Tags:
Categories: Desarrollo Web
Posted by Oscar.SS on sábado, 12 de diciembre de 2009 11:56
Permalink | Comentarios (0) | Post RSSRSS comment feed

Enviar datos asíncronos a un control fuera de un UpdatePanel

Es posible que a muchos este título les suene a chino, y quizás también, no sea un caso que se de muy a menudo. Pero opino que es una característica que debemos conocer para tener más control sobre las aplicaciones ASP.NET AJAX.

 

El problema

Comenzemos plantenado el problema. Cuando utilizamos AJAX Extensions en una aplicación ASP.NET sabemos que en la respuesta asíncrona del servidor solo se envía de vuelta al cliente web una porción muy pequeña de HTML. Exactamente, solo se envía el HTML necesario para actualizar los controles que se encuentran dentro de un UpdatePanel, es lo que se conoce como repintado parcial. ¿Pero que ocurre si queremos, en algunas ocasiones, actualizar un control que se encuentra fuera de un UpdatePanel?. Es decir, ¿como podemos decirle al servidor que envíe otros datos además del HTML del repintado parcial?.

Veámoslo con un pequeño ejemplo.

    <div>
        
<asp:TextBox ID="TextBox1" runat="server">
            
</asp:TextBox><asp:Button ID="Button1" runat="server" Text="Button" 
            onclick
="Button1_Click" />
        <
br />
        <
br />
    
<!-----------Sección que se actualiza por AJAX (repintado parcial)--------------->
        
<asp:UpdatePanel ID="UpdatePanel1" runat="server">
            
<ContentTemplate>
                
<asp:Label ID="Label1" runat="server" Text="Label"></asp:Label>
            
</ContentTemplate>
            
<Triggers><asp:AsyncPostBackTrigger ControlID="Button1" EventName="Click" /></Triggers>
        
</asp:UpdatePanel>
    
<!-------------------------------------------------------------------------------->    
        
<br />
        <
br />
        <
asp:Label ID="Label2" runat="server" Text="Label"></asp:Label>
    
</div>

 

¿Que tenemos aquí?. Tenemos un TextBox donde el usuario introducirá su nombre y tenemos dos Labels que mostrarán un saludo al usuario cuando hagan Click sobre el Button. Como podemos observar uno de los Labels se encuentra dentro de un UpdatePanel y el otro fuera. El que esta dentro se aprovechará de la funcionalidad de AJAX para actualizar su contendido de manera asíncrona.

Añadamos código para manejar el evento del Button y veamos que sucede.

 

        protected void Button1_Click(object sender, EventArgs e)
        {
            
string datosEnviados "Hola " + TextBox1.Text;

            
Label1.Text datosEnviados;
            
Label2.Text datosEnviados;
        
}

 

Al ejecutarse el evento vemos que solo se actualiza el Label1. El Label2 no se entera de nada. ¿Porque?.

Ya lo hemos comentado ante. El servidor solo está enviando al cliente el HTML necesario para actualizar los controles que se encuentran dentro del UpdatePanel.Repito la pregunta, ¿como podemos decirle al servidor que envíe otros datos además del HTML del repintado parcial?.

 

La solución

Cambiamos el código del evento del Button por el siguiente:

            string datosEnviados "Hola " + TextBox1.Text;
            
Label1.Text datosEnviados;

            
ScriptManager1.RegisterDataItem(Label2, datosEnviados);

El método RegisterDataItem() del ScriptManager envía datos en una llamanda asíncrona a los controles que se encuentran fuera del UpdatePanel. Ahora debemos manegar el siguiente evento en el cliente.

    <script type="text/javascript" language="javascript">
        Sys.WebForms.PageRequestManager.getInstance().add_pageLoading(RecogerDatosHandler)
;

        function 
RecogerDatosHandler(sender, args) {
            
var datos args.get_dataItems();
            
$get("Label2").innerHTML datos["Label2"];
        
}
    </script>

En primer lugar obtenemos una instancia de la clase Sys.WebFroms.PageRequestManager que es la encargada de actualizar los controles que se encuentran dentro del UpdatePanel. Antes de que esta actualización ocurra, gestionamos el evento pageLoading() y recogemos los datos enviados desde el servidor para actualizar el Label2.

Hemos creado nuestro propio manejador del evento, RecogerDatosHandler(), que por medio del la colección args podemos acceder a los datos enviados desde el servidor.

Nota: La definición del script cliente anterior debe colocarse después de la definición del controls ScriptManager, de lo contrario nos dará un error diciendo que el espacio de nombres Sys no está definico aún, ¡como es lógico!.

 


Tags:
Categories: Desarrollo Web
Posted by Oscar.SS on martes, 08 de diciembre de 2009 17:48
Permalink | Comentarios (0) | Post RSSRSS comment feed

AJAX en ASP.NET 2.0 con AJAX Extensions

Quizás aquí algunos esperaban erroneamente que el título hablara de ASP.NET 3.5 pero como ya he comentado alguna vez (creo!!), las versiones de ASP.NET saltan de la 2.0 a la 4.0. No así las versiones de la plataforma .NET que si han seguido un camino más continuado 2.0, 3.0, 3.5, y 4.0.

Por lo tanto, este árticulos y los siguientes tratarán de lo que se puede hacer con AJAX en ASP.NET 2.0 con el framework 3.0 o 3.5. Por otra parte, igualmente válido son para aquellos que trabajan con ASP.NET 2.0 sobre .NET 2.0 y tienes instaladas las plantillas AJAX Extensions. (Ver gráfico evolución plataforma)

 

 

Código Cliente

En este caso el código cliente se redece a incluir un control ScriptManager y un control UdatePanel dentro del cuerpo de la página.

    <form id="form1" runat="server">
    
<asp:ScriptManager ID="ScriptManager1" runat="server">
    
</asp:ScriptManager>
    
<div>
        
<p style="text-align:center; font-size:xx-large; color:Blue; text-decoration:underline">AJAX con AJAX Extensions</p>
        
<span>Seleccionar un nombre:</span>
        
<asp:DropDownList ID="DropDownList1" runat="server" AutoPostBack="True" 
            onselectedindexchanged
="DropDownList1_SelectedIndexChanged">
            
<asp:ListItem Selected="True">Oscar</asp:ListItem>
            
<asp:ListItem>David</asp:ListItem>
            
<asp:ListItem>Diego</asp:ListItem>
            
<asp:ListItem>Sergio</asp:ListItem>
            
<asp:ListItem>Luis</asp:ListItem>
        
</asp:DropDownList>
        
<br />
        <
br />
        <
asp:UpdatePanel ID="UpdatePanel1" runat="server">
            
<ContentTemplate>
                Categoria profesional:  
<asp:Label ID="Label1" runat="server" ForeColor="Red"></asp:Label>
            
</ContentTemplate>
            
<Triggers><asp:AsyncPostBackTrigger ControlID="DropDownList1" 
                    EventName
="SelectedIndexChanged" /></Triggers>
        
</asp:UpdatePanel>
        
<br />
        <
br />
        <
span>Mientras se ejecuata la petición de datos escribir aquí para comprobar el AJAX.</span>
        
<input id="Text1" type="text" />
    </
div>
    
</form>


El control ScriptManager es el encargado de registrar en el cliente todo el código script necesario para soportar AJAX.  Debe aparecer antes que ningún otro control en la página.

En el control UpdatePanel, dentro de <ContentTamplate>, incluiremos únicamente el contenido HTML que deba ser actualizado cuando el servidor devuelva los datos. Este control se encarga de enviar desde el servior solo la porción de HTML que contiene, realizando lo que se denomina un repintado parcial de la página. Dentro de <Triggers> se declaran los controles y eventos que disparan este repintado parcial.

¡Y ya esta!. No hay que hacer nada más. No hay que escribir ni una linea de código cliente, entre el control ScriptManager y el UpdatePanel, se encargar de envíar los datos al servidor y de recibirlos mostrandolos al usuario.

 

Código Servidor

Y en el servidor no tenemos que hacer nada especial para manipular la funcionalidad AJAX. Simplemente incluiremos el código necesario dentro del evento del control que hemos declarado anteriormente dentro de <Triggers> en el UpdatePanel.

    protected void DropDownList1_SelectedIndexChanged(object sender, EventArgs e)
    {
        
string rutaDatos Server.MapPath("~/App_Data/datos.xml");

        string 
nombreSelec DropDownList1.SelectedValue;

        
//Antes de enviar la petición simulamos una tarea larga.
        
System.Threading.Thread.Sleep(5000);

        
Label1.Text ManejarDatos.ObtenerCategoria(nombreSelec, rutaDatos);
    
} 


Código de descarga 

El código completo mencionado en este artículo se encuentra en la carpeta AJAX_NET_3.5/AJAX_Extensions del siquiente enlace. Para probar la aplicación ejecutar la página AJAX_Extensions.aspx de la carpeta mencionada.

AJAX_ASP.NET.rar (15,45 kb)


Tags:
Categories: Desarrollo Web
Posted by Oscar.SS on sábado, 14 de noviembre de 2009 13:03
Permalink | Comentarios (0) | Post RSSRSS comment feed

AJAX en ASP.NET 2.0 con Script CallBacks

Con la llegada de ASP.NET 2.0 se puso a nuestra disposición otra técnica para realizar llamadas en segundo plano desde el cliente. Esta técnica es denominada Script CallBacks. La ventaja más notable de esta forma de implementar AJAX es que nos evita tener que escribir tanto código cliente. En concreto, nos evita tener que escribir todo el código JavaScript para crear, configurar y enviar los datos al servidor con XMLHTTPRequest, tal y como vimos en el post anterior. Naturalmente si tendremos que utilizar código cliente para manipular los datos una vez devueltos por el servidor.

Comencemos recordando la interfaz de usuario que teníamos en el ejemplo anterior.

  

 

Código Servidor

Antes de nada, debemos saber que la técnica de Script CallBacks se apoya en la interfaz ICallbackEventHandler. Por lo tanto debemos implementar esta interfaz en la página que recibe la llamada asíncrona. Más tarde veremos cuales son los métodos de esta interfaz.

Ya hemos comentado antes que no necesitamos escribir todo el JavaScript a mano para enviar los datos asíncronamente al servidor. Pero entoces...¿como enviamos los datos?. En realidad, desde el cliente si llamaremos e inviaremos los datos al servidor pero todo esto lo configuraremos desde el propio servidor. Veamos como.

         this.listaNombres.Attributes["onchange"ObtenerFuncionCliente();

En primer lugar, asignamos al evento onchange de la lista de nombres una cadena que será devuelta por la función ObtenerFuncionCliente(). Esta cadena contiene el nombre de una función JavaScript proporcionada por ASP.NET. Esta función cliente será la encargada de realizar la llamada asíncrona y de enviar los datos al servidor. La función toma la forma siguiente:

 

WebForm_DoCallback("Control", "DatosEnviados", "FuncionRecogerDatos", "idLlamada", "FuncionRecogerErrores", "Asíncrono")

 

  • Control (string). Nombre del control que implementa la interfaz ICallbackEventHandler.
  • DatosEnviados (string). Cadena con los datos enviados desde el cliente al servidor.
  • FuncionRecogerDatos (string). Nombre de la función cliente que será la encargada de recoger y manipular debidamente los datos recibidos desde el servidor.
  • idLlamada (string). Parámetro opcional, un identificador único de esta llamada. Sirve para diferenciarla de otras llamadas en contextos de llamadas múltiples. Puede ser null.
  • FuncionRecogerErrores (string). Nombre de la función cliente encargada de recoger y manipular los errores producidos.
  • Asíncrono (bool). Determina si la llamada será realizada de forma asíncrona o no.
Pero como ya hemos comentado, esta función de JavaScript y todos sus parámetros serán configurados con código servidor.
  
    private string ObtenerFuncionCliente()
    {
        
//Obtener los parámetros de la función cliente.
        
var control = this;

        string 
datoRecibido = string.Format("document.getElementById('{0}').value"this.listaNombres.ClientID);
        
        string 
FuncionCliente "RecogerRespuestaCallBack";

        string 
idLlamada "null";

        string 
FuncionClienteError "MostrarError";

        bool 
llamadaAsincrona = true;

        
//Configurar la función cliente con los parámetros anteriores.
        
string funcionConfigurada Page.ClientScript.GetCallbackEventReference(
            control, datoRecibido, FuncionCliente, idLlamada, FuncionClienteError, llamadaAsincrona)
;

        
//Devolvemos la instrución JavaScript con el nombre de la función que realiza la llamada.
        
return string.Format("javascript:{0}", funcionConfigurada);
    
}

El método GetCallbackEventReferecen() devuelve una referencia a la función cliente WebForm_DoCallback() que hemos comentado antes. Entendamos lo que hemos hecho hasta ahora.

En la primera petición a nuestra página, esta devolverá entre todo el código HTML un fragmento de JavaScript con la función WebForm_DoCallback() y todos sus parámetros. Esta función sera la encargada de enviar los datos al servidor cuando se produzca el evento onchange.

 

La interfaz ICallbackEventHandler implementa únicamente dos métodos: 

  • RaiseCallbackEnvent(). Esta función será llamada automaticamente cuando se reciba una llamada en segundo plano desde el cliente.
  • GetCallbackResult(). Devuelve una cadena con los datos y llama automaticamente a la función JavaScript encargada de recoger los datos en el cliente.

    public void RaiseCallbackEvent(string datosCliente)
    {
        datoRecibido 
datosCliente;
    
}

    
public string GetCallbackResult()
    {
        
string rutaDatos Server.MapPath("~/App_Data/datos.xml");

        
//Antes de enviar la petición simulamos una tarea larga.
        
System.Threading.Thread.Sleep(5000);

        return 
ManejarDatos.ObtenerRespuesta(datoRecibido, rutaDatos);
    
}

Código Ciente
Poco queda por decir. Ahora solo tendremos que implementar las dos funciones Java Script que especificamos al configurar los parámetros del método GetCallbackEventReferecen(). Una función para recoger y mostrar los datos al usuario y otra para el control de los posibles errores que se produzcan.
//Funcion que es llamada automaticamente para procesar la respuesta del servidor.
function RecogerRespuestaCallBack(xmlReturn, idLlamada) {
    
try {
       
//CargarXmlString() parsea una cadena en un documento XML.
        
var datosReturn CargarXmlString(xmlReturn).getElementsByTagName("categoria");
        
var resp document.getElementById("txRespuesta");
        
resp.innerText datosReturn[0].childNodes[0].nodeValue;
    
}
    
catch (ex) {
        MostrarError(ex)
;
    
}

}


//Funcion llamada automaticamente cuando se produce un error.
function MostrarError(ex) {
    alert(
"Error: " + ex);
}

 

Código de descarga 

El código completo mencionado en este artículo se encuentra en la carpeta AJAX_NET_2.0 del siquiente enlace. Para probar la aplicación ejecutar la página AJAX_ScriptCallBacks.aspx de la carpeta mencionada.

AJAX_ASP.NET.rar (15,45 kb)


Tags:
Categories: Desarrollo Web
Posted by Oscar.SS on viernes, 23 de octubre de 2009 22:14
Permalink | Comentarios (2) | Post RSSRSS comment feed

AJAX en ASP.NET 1.0 con XMLHTTPRequest

Este es el primero de una serie de artículos que escribiré sobre la implementación de un mismo ejemplo de AJAX en las diferentes versiones del framework.

Cuando veamos el ejemplo de AJAX con .NET Framework 3.5, podremos observar que todo esta muy encapsulado y aunque es muy intuitivo y productivo nos aleja bastante de lo que ocurre "por debajo".

Esto tiene dos consecuencias negativas, que tenemos menos control y que los programadores noveles, que comienzan sus primeros pasos directamente en la ultima versión de la plataforma no comprenden como funcionan conjuntamente estas tecnologías. Precisamente este es el motivo que impulsa esta serie de artículos.

¡Comencemos ya!. En primer lugar veamos la interfaz de usuario y expliquemos que pretendemos hacer. Recuerdo que el ejemplo será el mismo en todos los artículos de la serie pero implementados de forma diferente.

 


La aplicación es muy simple. El usuario selecciona un nombre de empleado y se devuelve por AJAX la categoría profesional del mismo. Estos datos están guardados en el servidor en un archivo XML. En el servidor, se ha relentizado la devolución de los datos para simular una tarea muy larga a la hora de obtener los datos.
 
El campo de texto inferior solo sirve para comprobar como a pesar de que se esta realizando una petición al servidor, el usuario puede seguir interactuando con la IU escribiendo al tiempo que los datos vuelven desde el servidor.
 
 
Código Cliente
El código cliente consiste en crear un objeto de tipo XMLHTTPRequest, configurarlo y enviar los datos al servidor. También implementaremos una función para recoger los datos devueltos por el servidor y mostrar el resultado. Mencionar que este código, al ser solo JavaScript, es compatible con cualquier plataforma, ya sea PHP, Java o .NET como en este caso.
Esta es la función que envía los datos al servidor.
function EnviarPeticion(datoEnviado) {

    
//Crear transmisor
    
try {
        transmisor 
= new ActiveXObject('Msxml2.XMLHTTP');
    
}
    
catch (ex1) {
        
try {
            transmisor 
= new ActiveXObject('Microsoft.XMLHTTP')//Inferior a IE7.
        
}
        
catch (ex2) {
            
try {
                transmisor 
= new XMLHttpRequest()//IE7, Mozilla FireFox, etc.
            
}
            
catch (ex3) {
                transmisor 
= false;
            
}
        }
    }

    
//Configurar el transmisor y enviar los datos.
    
if (transmisor != null) {
        transmisor.onreadystatechange 
RecogerRespuesta;

        
//Envio de datos con POST.
        
transmisor.open("POST""AJAX_XMLHTTPRequest.aspx"true);
        
transmisor.setRequestHeader("Content-Type""application/x-www-form-urlencoded");
        
transmisor.send("opcion=" + datoEnviado);  // param1=valor&param2=valor
    
}
}

En primer lugar creamos un objeto transmisor de tipo XMLHTTPRequest que nos permitirá comunicarnos con el servidor. Este objeto tiene diferentes implementaciones dependiendo del tipo de navegador cliente.

A continuación configuramos el transmisor para enviar los datos. La propiedad onreadystatechange determina cual será la función a la que se llamará automaticamente cuando los datos estén de vuelta desde el servidor.

La función open() abre la conexión con los siguientes parámetros, todos de tipo cadena menos un boleano:

  • POST/GET: método de envío del protocolo HTTP.
  • Página de servidor a la que se envía la petición. Es recomendable que esta página no contenga código HTML. Solo la declaración de la directiva @Page.
  • True/False (bool): Realizar una llamada asíncrona (true) o una llamada no asíncrona (false).
  • Usuario: Nombre de usuario. Este parámetro y el siguiente solo es necesario para páginas con autentificación de usuarios.
  • Contraseña: Contraseña del usuario. Opcional.
La función setRequestHeader() solamente es necesario si estamos enviando la petición por el método POST. Se encarga de especificar los atributos necesarios en la cabecera de la petición HTTP.
La función send() envía los datos al servidor en formato cadena. La cadena formada debe tener la forma param1=valor&param2=valor&param3=valor etc.
Con esto hemos realizado una llamada asíncrona hacia el servidor. Allí, lo veremos a continuación, se recogerá los datos enviados por el cliente, se manipularan debidamente y de devolverán al cliente. Concretamente a la función cliente especificada en la propiedad onreadystatechange.
function RecogerRespuesta() {
    
if (transmisor.readyState == 4)    // 4 = completado
    
{
        
if (transmisor.status == 200)    // 200 = OK
        
{
            
var datosReturn transmisor.responseXML.getElementsByTagName("categoria");
            var 
resp = document.getElementById("txRespuesta");
            
resp.innerText datosReturn[0].childNodes[0].nodeValue;
        
}
        
else //En caso de error
        
{
            
alert("Error: " + transmisor.status + " / " + transmisor.statusText);
        
}
    }
}

Primero comprobamos que la petición a finalizado con la propiedad readyState y a continuación comprobamos que no se han producido errores con la propiedad status.

Con la propiedad responseXML recogemos los datos como un documento XML dado que así han sido enviados desde el servidor. También pueden ser recogidos como una cadena con la propiedad responseText en el caso de que solo se enviará desde el servidor una cadena.

Accedemos al nodo que nos interesa del documento XML con la función getElementByTagName() y utilizando el DOM lo mostramos por pantalla.

 

Código Servidor 

El código cliente consiste básicamente en recoger los datos con el objeto Request, leer el archivo XML y buscar en el dato requerido. Construir luego un XML con el dato requerido y devolverlo al cliente con el método Response.Write().

Este es un ejemplo del código que se ejecutará en el evento Load() de la página.

        //Recogemos el dato enviado desde el cliente.
        
string datoRecibido Request.Params["opcion"];

        
// Se devuelve como XML.
        
Response.ContentType "text/xml";
        
// Evitar que haya caché.
        
Response.AppendHeader("cache-control""no-cache");
        
Response.Expires -1;

        
//Antes de enviar la petición simulamos una tarea larga.
        
System.Threading.Thread.Sleep(5000);

        
//Enviamos y cerramos la respuesta.
        
Response.Write(ManejarDatos.ObtenerRespuesta(datoRecibido, rutaDatos));
        
Response.End();

No hay mucho que comentar. Indicar que ManejarDatos es una clase con métodos estáticos que encapsula toda la funcionalidad para recuperar el dato requerido desde el XML en el servidor y con este dato construir el XML  que será devuelto al cliente.

 

Código de descarga 

El código completo mencionado en este artículo se encuentra en la carpeta AJAX_NET_1.x del siquiente enlace. Para probar la aplicación ejecutar la página index.htm de la carpeta mencionada.

AJAX_ASP.NET.rar (15,45 kb)


Tags:
Categories: Desarrollo Web
Posted by Oscar.SS on domingo, 18 de octubre de 2009 15:51
Permalink | Comentarios (2) | Post RSSRSS comment feed