Atlas, AJAX para ASP.NET: el UpdateProgress

Otra maravilla del Atlas es el control UpdateProgress... el propio nombre lo define, no?

Simplemente se trata de un control que muestra un mensaje mientras dura una ida y vuelta AJAX al servidor.

Siguiendo con el ejemplo explicado en el artículo sobre el UpdatePanel, tendremos un Button y una Label. La Label estará dentro del ContentTemplate de un UpdatePanel, y en el apartado de Triggers del UpdatePanel, cazamos el evento Click del Button, de modo que el efecto final será que cuando se haga clic sobre el Button, la Label actualizará su valor a la hora en que estamos.

Y para conocer el efecto del UpdateProgress, vamos a imponer que la ida y vuelta al servidor tarde 5 segundos, y mientras tanto queremos que se muestre el mensaje "Cargando Datos...". El código es más corto que la propia explicación:

NuestroCodeBehind.aspx.cs
    protected void Button1_Click(object sender, EventArgs e)
    {
        Label1.Text = DateTime.Now.ToString();
        System.Threading.Thread.Sleep(5000);
    }


NuestroEjemplo.aspx
        <atlas:ScriptManager ID="ScriptManager1" runat="server" EnablePartialRendering="true">
        </atlas:ScriptManager>
        <atlas:UpdateProgress ID="UpdateProgress1" runat="server">
            <ProgressTemplate>
                Cargando datos...
            </ProgressTemplate>
        </atlas:UpdateProgress>
        <asp:Button ID="Button1" runat="server" Text="Button" OnClick="Button1_Click" />
        <atlas:UpdatePanel ID="UpdatePanel1" runat="server" Mode="Conditional">
            <ContentTemplate>
                <asp:Label ID="Label1" runat="server" Text=""></asp:Label>           
            </ContentTemplate>
            <Triggers>
                <atlas:ControlEventTrigger ControlID="Button1" EventName="Click" />
            </Triggers>
        </atlas:UpdatePanel>


Como vemos, dentro del UpdateProgress definimos un ProgressTemplate, dentro del cuál podemos poner absolutamente lo que queramos. Desde un simple texto (como es el caso) hasta una imagen o todo lo que se nos ocurra.

De hecho, lo único que ocurre es que lo que está dentro del UpdateProgress estará dentro de un <div /> con style="display:none;", y cuando se ejecuta una llamada AJAX, lo que hay dentro de ese div se mostrará, hasta que la llamada AJAX sea devuelta y de nuevo deje de mostrarse.



Atlas, AJAX para ASP.NET: el TimerControl

Tras el artículo sobre el UpdatePanel de Atlas, tenemos claro que es éste es la estrella de los controles Atlas para ASP.NET... y con él se pueden utilizar otros muchos controles más sencillos, pero muy útiles.

Uno de los más chulos es el TimerControl. Con un simple arrastre de ratón, podemos configurar de manera muy sencilla que cada X milisegundos se ejecute un PostBack AJAX. Si unimos esa característica del TimeControl a un UpdatePanel, tenemos que cada X milisegundos sucede un PostBack, y los elementos que estén dentro del ContentTemplate de uno (o varios!!) UpdatePanel's sean modificados como nosotros deseemos. Por ejemplo, vamos a hacer una página que cada 2 segundos actualice la fecha y hora que estará escrita en un Label:

NuestroCodeBehind.asp.cs

    protected void TimerControl1_Tick(object sender, EventArgs e)
    {
        Label1.Text = DateTime.Now.ToString();
    }


NuestroEjemplo.aspx
        <atlas:TimerControl ID="TimerControl1" runat="server" Interval="2000" OnTick="TimerControl1_Tick">
        </atlas:TimerControl>
        <atlas:UpdatePanel ID="UpdatePanel1" runat="server" Mode="Conditional">
            <ContentTemplate>
                <asp:Label ID="Label1" runat="server" Text=""></asp:Label>           
            </ContentTemplate>
            <Triggers>
                <atlas:ControlEventTrigger ControlID="TimerControl1" EventName="Tick" />
            </Triggers>
        </atlas:UpdatePanel>


Como veis, configurar el TimerControl es muy sencillo, pues no hay más que indicar cada cuantos milisegundos se efectuará el PostBack AJAX.

En cuanto al UpdatePanel, tenemos que dentro de nuestro ContentTemplate estará el elemento que vamos a modificar (el Label) y en el apartado de Triggers indicamos que capture el control TimerControl1 cuando ejecute el evento Tick.

Y ya está, así de sencillo es. Ahora ya queda en vuestra imaginación las aplicaciones que sepais darle a esto. Ahí van algunas sugerencias:
- Comprobar cada X milisegundos si un usuario está online de verdad, olvidándose así de ser esclavo de variables de sesión que caducan cada Y minutos.
- Un sencillo chat o messeger entre usuarios.
- Un reloj .
- Tickers con noticias que cambian cada X segundos.
- AdRotators que cambien la publicidad cada cierto tiempo.
- Largo etcétera.



Atlas, AJAX para ASP.NET: el UpdatePanel

El UpdatePanel es, sin duda, uno de los controles estrella que nos trae Atlas. Es el que más directamente nos proporciona la posibilidad de tener una página 100% AJAX sin tener ni la más remota idea de lo que es AJAX.

La idea no es nueva, ya que por ejemplo, los chicos de MagicAjax vienen llevándola a cabo desde hace mucho tiempo. Todo está basado en "Lo que esté dentro de este UpdatePanel tendrá un comportamiento AJAX"... sin más.

Pongamos un ejemplo específico, el típico con un Button y una Label, y cuando aprietas el Button, la Label se actualiza con la fecha y hora actuales.

Hecho del modo tradicional sería:

<%@ Page Language="C#" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<script runat="server">
    protected void Button1_Click(object sender, EventArgs e)
    {
        Label1.Text = DateTime.Now.ToString();
    }  
</script>

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Página de ejemplo</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <asp:Button ID="Button1" runat="server" Text="Button" OnClick="Button1_Click" />
        <asp:Label ID="Label1" runat="server" Text=""></asp:Label>
    </div>
    </form>
</body>
</html>


Y para conseguir un efecto AJAX, no hay más que arrastrar un par de controles y tenemos lo siguiente:

    <form id="form1" runat="server">
    <div>
        <atlas:ScriptManager ID="ScriptManager1" runat="server" EnablePartialRendering="true">
        </atlas:ScriptManager>

        <atlas:UpdatePanel ID="UpdatePanel1" runat="server" Mode="Conditional">
            <ContentTemplate>

                <asp:Button ID="Button1" runat="server" Text="Button" OnClick="Button1_Click" />
                <asp:Label ID="Label1" runat="server" Text=""></asp:Label>           
            </ContentTemplate>
        </atlas:UpdatePanel>

    </div>
    </form>

Como veis no hay que cambiar nada del código, simplemente hay que cambiar (con simples movimientos de ratón) nuestro <form />... sencillísimo, ¿no?

Yendo parte por parte, lo primero que tenemos es el control "ScriptManager", que es el encargado de manejar todos los controles Atlas que añadamos en nuestra página. En este caso particular, sólo tenemos el control UpdatePanel, pero podría haber otros.

Fijaos en el importante atributo "EnablePartialRendering", que por defecto no está y se supone false. Para habilitar funcionalidades AJAX hay que ponerlo a true.

Por otra parte tenemos a nuestra estrella: el UpdatePanel. Lo hemos puesto con Mode="Conditional". Por defecto no está así, y está en mode "Always":
- Si está en Always, nuestro UpdatePanel se actualizará siempre que haga un PageLoad, por ejemplo a consecuencia de una actualización de otro UpdatePanel.
- Si está en Conditional, nuestro UpdatePanel sólo se actualizará cuando los elementos de dentro de nuestro UpdatePanel lo requieran.

Dentro del UpdatePanel está el <ContentTemplate /> que es donde irá nuestro contenido, en este caso el Button y la Label. Como os digo, la filosofía es muy sencilla, todo lo que haya dentro del UpdatePanel producirá comportamiento AJAX, y eso significa "cazar" los elementos que produzcan eventos (El Button produce el evento Click) y los elementos que de algún modo son cambiados (El texto de nuestra Label es modificado).

Y esto es muy importante, pues a los programadores nos interesa que un elemento sea modificado bien porque se ha lanzado un evento, bien porque otro elemento ha sido modificado. Y digo esto porque dentro del UpdatePanel, a parte del <ContentTemplate /> tenemos el apartado de <Triggers />.

Los Triggers nos sirven para definir qué eventos o modificaciones debe cazar el UpdatePanel para modificarse... veamos otro ejemplo:

        <asp:Button ID="Button1" runat="server" Text="Button" OnClick="Button1_Click" />
       
        <atlas:UpdatePanel ID="UpdatePanel1" runat="server" Mode="Conditional">
            <ContentTemplate>
                <asp:Label ID="Label1" runat="server" Text=""></asp:Label>           
            </ContentTemplate>
            <Triggers>
                <atlas:ControlEventTrigger ControlID="Button1" EventName="Click" />
            </Triggers>
        </atlas:UpdatePanel>


En esta ocasión, el Button ya no está dentro del ContentTemplate del UpdatePanel, por lo que en teoría apretar sobre el Button supondría que la página se recargara totalmente. Sin embargo, en el apartado de Triggers del UpdatePanel, indicamos que deseamos cazar el evento Click del control Button1.

Tened en cuenta que en principio puede parecer una tontería: ¿Para qué quiero un apartado Triggers si me vale con poner el botón dentro del ContentTemplate? Pero no lo es, porque nuestro ejemplo es trivial, pero en una página más elaborada, el elemento que produce el evento no tiene porque estar cerca del elemento que es modificado. Entre medias de ambos pueden haber muchísimas cosas, y el UpdatePanel cuanto más pequeño, mejor que mejor: menos gasto de ancho de banda y por tanto mayor rapidez.

Sabiendo esto, aunque del UpdatePanel hay muchos más aspectos que averiguar, ya se pueden conseguir complejos y eficientes comportamientos AJAX con un simple entramado de cuatro o cinco UpdatePanel's con varios Triggers definidos y reducidos ContentTemplate's.

Durante sucesivos artículos, iré poniendo ejemplos prácticos de Webs en las que estoy trabajando



Atlas: AJAX para ASP.NET de forma muuuuy sencilla

Atlas promete ser el punto de inflexión necesario para que las Web que usan AJAX dejen de ser algo exclusivo de las grandes empresas de Internet o los webmaster con conocimientos más avanzados.

Gracias al framework que Atlas propone, casi cualquier persona será capaz de realizar webs 100% AJAX sin tener ningún conocimiento de AJAX... ni siquiera, de javascript!!

Y lo mejor de todo es que configurar una web ya hecha para que utilice el poder de Atlas... es sólo cuestión de un par de minutos!!

Simplemente hay que copiar el archivo .dll y añadir varias líneas a tu web.config... Además, Atlas es gratis!!

Todo lo que queráis saber sobre Atlas, lo tenéis disponible en atlas.asp.net, desde donde podréis desde descargaros el framework, hasta ver vídeos. Uno de los vídeos "estrella" (éste) muestra cómo en 18 minutos se puede hacer un sistema de gestión de citas y notas totalmente AJAX.

Yo, lo que os aconsejo es que os descarguéis e instaléis el paqueta de Atlas desde aquí. De ese modo se añadirá un template Atlas a vuestro programa de la familia VisualStudio. Si queréis empezar desde cero una web Atlas no tenéis más que empezar desde ese template, y si lo que queréis es adaptar alguna web que tengáis ya hecha:
- Empezad una nueva web con el template Atlas
- Copiaos el archivo .dll que se ubica en el directorio bin
- Adaptar vuestro web.config al web.config que se ha creado en el template Atlas

Duración aproximada de la adaptación: 2 minutos.

Pero como todo no podía ser bueno, he de avisaros que de momento Atlas no está en su versión final, simplemente es un CTP (ahora mismo el de Abril)... además, para utilizar Atlas al 100% usando vuestro programa de la familia VisualStudio (en mi caso Visual Web Developer Express), hace falta que usar ASP.NET 2.0

Para aprender a utilizar Atlas, de momento os remito a su Tutorial Oficial, pero pronto pondré algunos ejemplos sencillos de Atlas en este blog.



El AppendFormat() del StringBuilder y string.Format()

Como ya se ha comentado en otros artículos como El StringBuilder, en el mundo de la programación Web, es muy común trabajar con largas cadenas de texto.

Ya sabemos que el StringBuilder es el mejor modo de concatenar mucho texto, ya que la concatenación de varios strings queda descartada tras la Prueba de eficiencia: StringBuilder vs. Concatenación de Strings.

Pero no sólo de eficiencia vive la programación Web, también hay que hacer énfasis tanto en el mantenimiento interno de nuestra web como de nuestra comodidad a la hora de programar.

Y el AppendFormat() del StringBuilder es una mejora muy sustanciosa. Veamos un simple ejemplo.

Imaginemos que queremos crear una frase con varias variables:

ejemploPesado.aspx
        StringBuilder sb = new StringBuilder();
        string nombre1 = "Samuel Etoo";
        int goles1 = 24;
        string nombre2 = "David Villa";
        int goles2 = 23;

        sb.Append("El jugador del Barça, " + nombre1 + ", ha marcado " + goles1);
        sb.Append(" goles, mientras que " + nombre2 + " ha marcado " + goles2 - goles1);
        sb.Append(" " + goles2 > goles1 ? "más" : "menos");



Vemos que a la hora de construir la frase, se mezclan variables y texto literal, de modo que llega a ser bastante lioso.
Ahora veremos la misma versión de nuestro pequeño algoritmo utilizando el AppendFormat del StringBuilder:

ejemploSencillo.aspx
        StringBuilder sb = new StringBuilder();
        string nombre1 = "Samuel Etoo";
        int goles1 = 24;
        string nombre2 = "David Villa";
        int goles2 = 23;

        sb.AppendFormat("El jugador del Barça, {0} ha marcado {1} goles", nombre1, goles1);
        sb.AppendFormat(", mientras que {0} ha marcado {1} {2}", nombre2, goles2 - goles1, goles2 > goles1 ? "más" : "menos");


Vemos que es una versión mucho más agradable a la vista, y por tanto ayuda a nuestra comodidad y al mantenimiento de nuestra aplicación.

Por otra parte el uso del AppendFormat es muy sencillo, y se entiende sólo con ver el ejemplo. No hay más que poner un número entre llaves, y ahí se colocará la variable correspondiente empezando desde la siguiente coma a nuestro string.

Por otra parte, si no nos es necesario utilizar el StringBuilder, podemos hacer lo mismo con string.Format(), de un modo muy similar:

ejemploConString.aspx
       string texto = "Hola mundo";
       string mensaje = string.Format("El {0} es el primer ejemplo típico en programación", texto);


Feliz código



AntiSpamMail: un control de usuario para prevenir el Spam de tu mail

Una de las formas de Spam más detestables son las que se hacen por mail: ¿¿qué porcentaje de mails que recibís son Spam?? En su mayor parte, quien os envía el Spam consigue vuestras direcciones gracias a robots que van rastrando la Web en busca de direcciones de correo: ¿cuántas veces habéis dejado vuestras direcciones en foros, blogs, páginas personales, etc.?

En la sección de controles de usuario de es-asp.net he publicado un control web que os puede ser muy útil para prevenir que los robots rastreen vuestras webs en busca de correos (tanto vuestros como de vuestros usuarios) y de ese modo se creen bases de datos de Spam de mails. La url directa es: http://www.es-asp.net/controles-de-usuario/239.aspx

Desde ahí podréis bajaros tanto el fichero .dll, como el proyecto entero del VisualStudio (en mi caso Visual C# Express). Además, se os explica cómo hacerlo funcionar correctamente.

Lo único que hace el antiSpamMail, que funciona como control web y también como una clase a instanciar, es coger la dirección de correo que le asigneis y transformarla en un código javascript que los robots no saben leer.

De modo que os dejo que le echéis un ojo al código y os animo a utilizarlo para evitar más una de las grandes lacras de la red: el Spam.



Google Code Jam Europe 2006

Desde el 1 de mayo se han abierto las inscripciones para el primer Google Code Jam en Europa.

Se trata de una competición entre los mejores programadores del mundo con varias fases:
1ª- Participa todo el que quiere y pasan sólo 500 (OnLine)
2ª- Participan 500 y pasan 250 (OnLine)
3ª- Participan 250 y pasan 50 (OnLine)
Final- 50 personas se enfrentan cara a cara en Dublin, con todos los gastos pagados y 30.000 € en premios.

Pueden participar todo los europeos que quieran (los no-europeos tendrán que esperar otras competiciones )

Personalmente os animo a que participeis, pues es muy divertido y apenas "pierdes" 60 minutos que dura el examen.
Para la info oficial, visitar la web oficial del evento, o directamente ir aquí , que es donde se hace todo.

Ánimo y suerte!




AJAX a pelo: XMLHttpRequest

En mi anterior artículo Pero qué xxxxxx es AJAX!!!!!??????, os introducía al mundo de AJAX y sus Frameworks para .NET. Sin embargo, a mí siempre me ha gustado conocer el fondo de las cosas, por lo que no me vale saber que dicho Framework funciona y ya está.

En la parte final de todo Framework AJAX, nos encontramos con XMLHttpRequest, con el que podemos enviar y recibir información de cliente a servidor sin recargar la página.

Cabe aclarar que nos vamos a mover en el mundo de javascript, todo lo haremos desde ahí en la parte del cliente, y en la parte del servidor podemos utilizar ASP.NET o cualquier otra tecnología web.

Lo curioso de todo es que usar AJAX a pelo con XMLHttpRequest es en realidad muy sencillo. Nosotros lo vamos a organizar en cuatro fases:

1.- Crear el objeto XMLHttpRequest
2.- Enviar la información
3.- Tratar la información en servidor
4.- Recoger la información en cliente

1.- Crear el objeto XMLHttpRequest
Como casi siempre en javascript, el XMLHttpRequest se obtiene de manera distinta para los distintos navegadores, pero el código más o menos "estándar" para estos quehaceres es:

var xmlHttp;
function CreateXmlHttp()
{
   
    // Probamos con IE
    try
    {
        // Funcionará para JavaScript 5.0
        xmlHttp = new ActiveXObject("Msxml2.XMLHTTP");
    }
    catch(e)
    {
        try
        {
            xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
        }
        catch(oc)
        {  
            xmlHttp = null;
        }
    }

    // Si no se trataba de un IE, probamos con esto
    if(!xmlHttp && typeof XMLHttpRequest != "undefined")
    {
        xmlHttp = new XMLHttpRequest();
    }

    return xmlHttp;
}


Donde vemos que primero probamos a crear el objeto para navegadores IE en su diferentes versiones, y si no es así (casi siempre FireFox) lo hacemos para el resto de navegadores. Hasta ahora todo muy sencillo.

2.- Enviar la información
Ahora lo que vamos a hacer es enviar la información del cliente a servidor:

function envioInfo(txt)
{
    // 1.- Creamos el objeto xmlHttpRequest
    CreateXmlHttp();
   
    // 2.- Definimos la llamada para hacer un simple GET.
    var ajaxRequest = 'receptor.aspx?Info=' + txt;

    // 3.- Marcar qué función manejará la respuesta
    xmlHttp.onreadystatechange = recogeInfo;
  
    // 4.- Enviar
    xmlHttp.open("GET", ajaxRequest, true);
    xmlHttp.send("");
}


Como vemos, lo que estamos haciendo es:
1.- Recoger la instancia del objeto XMLHttpRequest
2.- Asignar la URL donde mandaremos la petición y guardar en su QueryString la info que vamos a mandar
3.- Indicar qué función manejará la respuesta que nos devuelva el servidor (lo veremos más adelante)
4.- Enviamos la información. En nuestro ejemplo hacemos:

    xmlHttp.open("GET", ajaxRequest, true);

Lo que indica que utilizaremos el método GET, enviaremos la info a la url definida y sí queremos que se comporte de forma asíncrona (marcar false para exigir modo síncrono).

Posteriormente no tenemos más que mandar la petición con la función Send();

Sí, parece sencillo, pero hagamos dos observaciones importantes:

1º. Estamos utilizando el método GET, y uno de los aspectos a tener en cuenta es que si queremos enviar mucha información es posible que tengamos problemas. Por tanto, estaría bien poder hacer las peticiones vía post.
2º. Asynchronous JavaScript And XML... dónde está el XML aquí!!!??? En nuestro ejemplo mandábamos sólo ua poca info, pero si quisiéramos mandarle al servidor la discografía entera de los Beatles, tendríamos que pensar en la utilidad de XML como contenedor de información.

Veamos pues un ejemplo en el que se utiliza tanto el método POST como XML para mandar info.

function envioInfoPost(XML)
{
    // 1.- Creamos el objeto xmlHttpRequest
    xmlHttp = CreateXmlHttp();
   
    // 2.- Definimos la llamada.
    var ajaxRequest = 'receptor.aspx';

    // 3.- Marcar qué función manejará la respuesta
    xmlHttp.onreadystatechange = recogeInfo;
  
    // 4.- Enviar
    xmlHttp.open("POST", ajaxRequest, true);
    xmlHttp.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=ISO-8859-1');
    xmlHttp.send(XML);
}


Cómo veis, poco cambia con respecto al anterior ejemplo. Ahora indicamos explícitamente que queremos que la info se envía vía POST, le hacemos ver al navegador cliente que estamos enviando un formulario y finalmente vemos que el XML se envía dentro de la función send.

3.- Tratar la información en servidor
Tratar la información en servidor es también sencillo.
Recoger la información que se ha enviado no supone más que hacer en Request.QueryString si se ha enviado vía GET o un Request.Form si se ha hecho vía POST.

El modo en que tratemos esa info ya corre por cuenta nuestra, y según si queremos devolver un fichero XML o un simple texto llano haremos una cosa u otra... pero basta de palabreo y veamos un ejemplo:

    private void recogeInfoServidor()
    {
        string info = Request.QueryString["info"];
        //string info = Request.Form[0];
        string XML = Request.Form[0];

        /**************/
        // Tratar INFO
        /**************/

        HttpResponse response = HttpContext.Current.Response;

        response.Clear();
        response.ContentType = "text/plain";
        //response.ContentType = "text/xml";
        response.Write(s);
        response.Flush();
        response.End();
    }


4.- Recoger la información en cliente
Tal y como habíamos indicado en...

    xmlHttp.onreadystatechange = recogeInfo;

... la función que se va a encargar de recoger la info será recogeInfo.

function recogeInfo()
{
    if(xmlHttp.readyState == 4 && xmlHttp.status == 200)
    {
        alert(xmlHttp.responseText);
    }
}

La instancia del XMLHttpRequest tiene dos propiedades muy útiles: readyState y status.

Status es bastante obvia, pues simplemente devuelve el número que indica el resultado de la petición de la página (200 indica que la petición ha sido resulta correctamente). También podemos hacer uso de statusText, que nos devuelve la misma info pero en formato texto (en el caso de 200 devolvería "OK").

ReadyState indica el estado en que se encuentra nuestra petición, y tiene 5 valores diferentes:
0 -> No Inicializado
1 -> Cargando
2 -> Cargado
3 -> Interactuando
4 -> Completado

Finalmente mostramos por pantalla el resultado en modo texto que nos devuelve el servidor mediante response.Text, pero si quisiéramos que nos devolviera el resultado en modo XML para trabajar con éste desde javascript, usaríamos responseXML.