viernes, 30 de marzo de 2012

JSON 2



En esta segunda entrada veremos cómo podemos enviar un objeto de forma asíncrona utilizando JSON.

Tomando el ejercicio anterior como preámbulo, utilizaremos el mismo objeto Product para realizar el envío de información.

El primer problema que podemos encontrar es que la notación JSON no es tan fácil de generar (no es algo del otro mundo pero definitivamente quita mucho tiempo).    Para facilitarnos el trabajo existe una librería llamada json.js que podemos descargar de aquí.

Esta librería expone una función llamada stringify que nos ayudará a traducir un objeto javascript a su notación JSON correspondiente.

Vamos a modificar nuestro handler AddProduct.ashx para que reciba el objeto y simule meterlo a una base de datos.    Primero debemos agregar referencia al espacio de nombres donde está definido el objeto JavaScriptSerializer:

using System.Web.Script.Serialization:

En el procedimiento ProcessRequest vamos a recibir el objeto JSON y lo vamos a "deserializar" al objeto Producto que tenemos en nuestra página web.

public void ProcessRequest(HttpContext context)
{
    string jsonProduct = context.Server.UrlDecode(Uri.UnescapeDataString(context.Request.Form.ToString()));
    JavaScriptSerializer jsSerializer = new JavaScriptSerializer();
    DataObjects.Product product = jsSerializer.Deserialize<DataObjects.Product>(jsonProduct);
    System.Threading.Thread.Sleep(2000);
}

En la primera línea de código estamos primero decodificando con la función UnescapeDataString para quitar todos los caracteres que comienzan con "%", por ejemplo %20 que es el espacio en blanco; después estamos utilizando la función UrlDecode para cambiar todos los "+" por espacios en blanco.    Al final de estas dos funciones obtenemos el código JSON que nos fue enviado desde el cliente.

Con la tercera línea de código estamos tomando el código JSON y lo estamos convirtiendo en el objeto Product que le corresponde en nuestro proyecto.    Las características del objeto Product permiten realizar esta operación (propiedades públicas de lectura y escritura cuyos nombres coinciden con las propiedades del objeto JSON).

Finalmente estamos llamando a la función Sleep para simular que estamos llevando a cabo un proceso de registro.

Ahora vamos a ajustar nuestro archivo Products.htm para que haga la llamada al servidor.   Primero agregaremos una referencia a la librería json.js:

<script type="text/javascript" language="javascript" src="js/json.js"></script>

Después agregaremos un botón para ejecutar la función que enviará el objeto al servidor:

<input type="button" value="Send product" onclick="return sendProduct()" />

Y finalmente crearemos nuestro function sendProduct que será la encargada de realizar el POST al servidor:

function sendProduct() {
    var myProduct = new Product(1, 'Product 1');
    $.ajax({
        'type': 'POST',
        'dataType': 'json',
        'data': JSON.stringify(myProduct),
        'url': '/Handlers/AddProduct.ashx',
        'success': sendProduct_success,
        'error': sendProduct_error
    });
}
function Product(id, name) {
    this.Id = id;
    this.Name = name;
}
function sendProduct_success(d) {
    alert('El producto ha sido registrado');
}
function sendProduct_error(d) {
    alert('Mmm... algo anda mal');
}

Podemos darnos cuenta que:
1. He creado un objeto personalizado llamado "Product" que está definido de igual forma que el que tenemos en DataObjects.Product en el servidor, esto es para evitar problemas de serialización/deserialización.
2. El tipo de la llamada es POST.
3. Estamos especificando que el tipo de información que estamos enviando es json.
4. Utilizamos la función JSON.stringify para traducir nuestr objeto myProduct a JSON.

Espero les haya sido de mucha utilidad.

domingo, 25 de marzo de 2012

Privacidad en facebook



Hubo cambios en las políticas de privacidad en facebook que pueden revelar nuestra información a aplicaciones aún cuando nosotros no lo hayamos autorizado de forma expresa.

Funciona así:
  1. Un amigo tuyo le da los permisos que la aplicación le está pidiendo.
  2. La aplicación podrá ver y tener acceso a la información que puede ver tu amigo.
¿Cómo arreglarlo?
  1. Inicio.
  2. Configuración de la privacidad.
  3. Aplicaciones y sitios web.
  4. Cómo las personas usan tu información en las aplicaciones que utilizan.
  5. Limpiar todas las casillas de verificación y guardar cambios.

Es importante limitar el acceso que tienen las aplicaciones a nuestra información... cuidado con su privacidad.

sábado, 24 de marzo de 2012

JSON



Cuando queremos implementar un modelo de comunicación asíncrono entre una página web y el servidor tenemos al menos dos opciones: JSON y XML a través de requests con distintos verbos dependiendo del objetivo de la llamada o ejecución.    Aún cuando sea algo muy sabido creo que es necesario mecionar que generalmente los verbos se utilizan de la siguiente forma:

GET: Leer
POST: Agregar / Actualizar
DELETE: Eliminar
PUT: Agregar / Actualizar

En este ejercicio estaremos ocupando los dos verbos más comunes que son el GET y el POST.    Para preparar el entorno agregaremos los siguientes elementos:

1. Crear carpeta js.
2. Agregar jquery.js a la carpeta js.
3. Crear carpeta Handlers.
4. Agregar Products.ashx y AddProduct.ashx a la carpeta Handlers.
5. Crear carpeta DataObjects.
6. Agregar clase Product a la carpeta DataObjects.
7. Agregar archivo Products.html al proyecto (opté por un HTML para dejar en claro que todo se hará a través de requests asíncronos y no a través de posts de una WebForm)

Si vemos el explorador de soluciones (Ctrl + Alt + L) veremos un arreglo de archivos como este:


Vamos a agregarle dos propiedades públicas de lectura y escritura a nuestra clase Product:

public class Product
{
    public int Id { get; set; }
    public string Name { get; set; }
}

Ahora vamos a crear el código necesario para que Products.ashx devuelva la lista de productos en JSON.    Para empezar vamos a agregar dos espacios de nombres, el que contiene a JavaScriptSerializer y a nuestra clase Product:

using System.Web.Script.Serialization;
using SendReceiveJSON.DataObjects;

Los handlers reciben las peticiones a través del procedimiento ProcessRequest.   Notemos que por defecto ya viene un código que dice el tipo MIME que se va a devolver y el contenido de la respuesta.    El tipo MIME de JSON es "application/json" por lo que deberemos fijar este valor en la propiedad ContentType de la propiedad Response.

context.Response.ContentType = "application/json";

Ahora vamos a llenar una lista de objetos Product y la vamos a convertir en JSON utilizando el objeto JavaScriptSerializer:

List<Product> result = new List<Product>();
for (int i = 0; i < 10; i++)
{
    result.Add(new Product() { Id = i + 1, Name = "Product " + (i + 1).ToString() });
}
JavaScriptSerializer js = new JavaScriptSerializer();
context.Response.Output.Write(js.Serialize(result));

Si ejecutamos directamente el handler Products.ashx desde el navegador deberemos ver un resultado como este:


Para terminar este primer ejercicio vamos a crear el código necesario en nuestra página Products.htm para que llame al handler y muestre el listado de productos.    Para esto necesitamos un botón y un contenedor donde insertaremos una tabla con los productos.

<input type="button" value="Get products" onclick="return getProducts()" />
<hr />
<div id="divProducts"></div>

En nuestra función getProducts haremos la llamada a Products.ashx (recuerden agregar el script jquery en el head):

<script language="javascript" type="text/javascript">
    function getProducts() {
        $.ajax({
            'type': 'GET',
            'url': '/Handlers/Products.ashx',
            'success': getProducts_success,
            'error': getProducts_error
        });
        return false;
    }
    function getProducts_success(d) {
    }
    function getProducts_error(d) {
    }
</script>

Con la función $.ajax (de jquery) podemos realizar peticiones asíncronas (con el verbo type) al recurso web identificado determinado a través de la propiedad url.    Cuando la ejecución es exitosa se mandará a llamar a la función getProducts_success y cuando haya un error se ejecutará la función getProducts_error.

Vamos a modificar las funciones getProducts_success y getProducts_error para darle significado al ejercicio:

function getProducts_success(d) {
    var products = '<table cellspacing="0" cellpadding="5">';
    products += '<tr><th>Id</th><th>Name</th></tr>';
    for (var i = 0; i < d.length; i++) {
        products += '<tr>';
        products += '<td>' + d[i].Id + '</td>';
        products += '<td>' + d[i].Name + '</td>';
        products += '</tr>';
    }
    products += '</table>';
    products = $(products);
    $('#divProducts').append(products);
}
function getProducts_error(d) {
    alert('There was an error during execution process');
}

Notemos que estamos ocupando las propiedades públicas de nuestro objeto Product (Id y Name), esto se debe a que jquery realiza la decodificación de JSON y genera el arreglo de objetos necesarios para mostrar la información.    Es por eso que se llama JSON (JavaScript Object Notation)

En la siguiente entrada veremos cómo enviar un objeto Product al servidor a través de un POST en notación JSON.

miércoles, 21 de marzo de 2012

Mensaje Javascript 2



En esta segunda entrada referente a cómo mostrar un mensaje al usuario de un sitio web, le agregaremos tres parámetros a nuestra función showMessage:

1. Tipo de mensaje, el cual determinará el color de fondo del contenedor del mensaje.
2. Tiempo, será la cantidad de milisegundos que el mensaje deberá estar visible.
3. Callback, será una referencia a una función que se desee ejecutar después de que el mensaje se destruya.

Tal como en el ejercicio anterior, iremos cubriendo uno a uno de los elementos hasta tener el script completo.

Para manejar un tipo de mensaje, vamos a agregar un parámetro msgType que podrá tener como valores: 'error', 'success' y habrá un color default para cuando no se reciba este parámetro.

function showMessage(msg, msgType)

Ahora modificaremos un poco el código que establece el estilo de nuestra variable message y esto se debe a que el color de fondo dependerá del tipo de mensaje:

message.css({
    'height': '20px',
    'width': '100%',
    'position': 'fixed',
    'top': '-28px',
    'left': '0px',
    'margin': '0px',
    'background-color':
        msgType == 'error' ? '#d83a28' : (msgType == 'success' ? '#5aa250' : '#5f8be3'),
    'color': '#FFFFFF',
    'font-size': '15px',
    'text-align': 'center',
    'padding-top': '4px',
    'padding-bottom': '4px',
    'font-family': 'Verdana'
}).text(msg);

Se diría que le estamos asignando el color de una forma poco elegante, pero podríamos hacerlo un poco mejor si creamos una función llamada getMsgColor que reciba como parámetro el tipo de mensaje a mostrarse y nos devuelva el código del color.    Con este cambio el código quedaría de la siguiente manera:

function getMsgColor(msgType) {
    switch (msgType) {
        case 'error': return '#d83a28';
        case 'success': return '#5aa250';
        default: '#5f8be3';
    }
}

function showMessage(msg, msgType) {
    var message = $('<div></div>');
    message.css({
        'height': '20px',
        'width': '100%',
        'position': 'fixed',
        'top': '-28px',
        'left': '0px',
        'margin': '0px',
        'background-color': getMsgColor(msgType),
        'color': '#FFFFFF',
        'font-size': '15px',
        'text-align': 'center',
        'padding-top': '4px',
        'padding-bottom': '4px',
        'font-family': 'Verdana'
    }).text(msg);
    ... aquí continúa la función showMessage

La ventaja es que el código queda mucho más limpio y será muy fácil el agregar un nuevo tipo de mensaje con su color respectivo.    Probemos nuestra nueva función con el botón que ya tenemos (del ejercicio anterior):

<input type="button" value="Show message" onclick="return showMessage('Código y datos', 'success')" />

Para cubrir el segundo punto agregaremos otro parámetro llamado timeout que determinará el tiempo (en milisegundos) que deberá mostrarse el mensaje:

function showMessage(msg, msgType, timeout)

Y antes de mostrar nuestor mensaje verificaremos si recibimos un valor para nuestro parámetro timeout o bien le asignamos el tiempo default:

if (!timeout) timeout = 3500;
message.animate(
    { 'top': '0px' },
    'normal',
    null,
    function() {
        window.setTimeout(function() {
            message.animate({ 'top': '-28px' }, 'normal', null, function() { message.remove(); });
        }, timeout);
    }
);

Finalmente también nuestra función queremos que pueda mandar a llamar a una función determinada por el usuario para cuando haya sido destruído el mensaje.    Vamos a agregar un parámetro llamado callback:

function showMessage(msg, msgType, timeout, callback)

Al final el código de nuestro ejercicio debería quedar como el que a continuación coloco:

<input type="button" value="Show message" onclick="return showMessage('Código y datos', 'success', 2000, sayHello)" />
<script type="text/javascript">
    function getMsgColor(msgType) {
        switch (msgType) {
            case 'error': return '#d83a28';
            case 'success': return '#5aa250';
            default: return '#5f8be3';
        }
    }
    function showMessage(msg, msgType, timeout, callback) {
        var message = $('<div></div>');
        message.css({
            'height': '20px',
            'width': '100%',
            'position': 'fixed',
            'top': '-28px',
            'left': '0px',
            'margin': '0px',
            'background-color': getMsgColor(msgType),
            'color': '#FFFFFF',
            'font-size': '15px',
            'text-align': 'center',
            'padding-top': '4px',
            'padding-bottom': '4px',
            'font-family': 'Verdana'
        }).text(msg);
        $(document.body).append(message);
        if (!timeout) timeout = 3500;
        message.animate(
            { 'top': '0px' },
            'normal',
            null,
            function() {
                window.setTimeout(function() {
                    message.animate({ 'top': '-28px' }, 'normal', null, function() { message.remove(); if (callback) callback(); });
                }, timeout);
            }
        );
    }
    function sayHello() {
        alert('Hola mundo!');
    }
</script>

Espero les sea de mucha utilidad.

jueves, 15 de marzo de 2012

Mensaje Javascript



Un problema o dilema común en los sitios web, es el decidir la estrategia para mostrar mensajes al usuario, ya sean informativos, correctivos, de alarma, etc.

La función alert de javascript se ha convertido en un dolor de cabeza ya que afecta la navegabilidad de nuestro sitio, aparte hay que tomar en cuenta que el icono del diálogo muestra algo que le puede dar a entender al usuario que algo malo está pasando.

A mí me ha gustado mucho utilizar una alternativa que me permite deslizar un mensaje en la parte superior y que después de un tiempo éste desaparece de forma automática.    No impide la navegabilidad del sitio, no es agresivo y es muy sencillo de programar.

Comencemos por colocar un elemento DIV en la parte superior de un documento HTML (con referencia a jquery) en blanco:

<div style="height: 20px; width: 100%; position: fixed; top: 0px; left: 0px; margin: 0px; background-color: #264182; color: #FFFFFF; font-size: 15px; text-align: center; padding-top: 4px; padding-bottom: 4px; font-family: Verdana;">Código y Datos</div>

Tenemos un contenedor de 20px de alto con un posicionamiento de tipo fixed que nos permitirá mostrar el mensaje sin importar la posición del scroll en el navegador del visitante.   Le hemos dado un padding superior e inferior para centrar verticalmente el mensaje.

Ahora vamos a agregar un botón que nos permita experimentar un poco con jquery para ir construyendo el código necesario para mostrar nuestro mensaje.

<br /><br /><input type="button" value="Hide message" onclick="return hideMessage()" />

He colocado unos saltos de línea para que el mensaje no tape a nuestro botón.

Ahora lo que haremos es una pequeña animación que moverá el mensaje hacia arriba hasta que desaparezca:

<script type="text/javascript">
    function hideMessage() {
        $('#message').animate({
            'top': -28
        });
        return false;
    }
</script>

Veamos que estamos moviendo 28px y no 20px, esto se debe a que tenemos que sumar los 4px del padding superior y los 4px del padding inferior que actualmente tiene nuestro elemento DIV.

Visitemos nuestra página y veremos que ya se oculta el mensaje.

Ahora agregaremos otro botón para mostrar el mensaje:

<input type="button" value="Display message" onclick="return displayMessage()" />

Y agregaremos su función (dentro del bloque <script>) javascript correspondiente:

function displayMessage() {
    $('#message').animate({
        'top': 0
    });
    return false;
}

Si volvemos a ejecutar nuestra página de prueba, veremos que ya podemos ocultar el mensaje mediante una animación y también podemos mostrarlo mediante una animación.

Bueno, tenemos las dos funciones básicas para mostrar el mensaje

Ahora generamos otro archivo HTML (con referencia a jquery) donde tendremos nuestro código final y agregamos un botón para mandar a llamar a nuestra función:

<input type="button" value="Show message" onclick="return showMessage('Código y datos')" />

Nuestra función showMessage(msg) deberá:

1. Construir un elemento DIV.
2. Establecer todas las propiedades para su estilo.
3. Ingresar el texto del mensaje en el elemento DIV.
4. Agregar nuestro elemento DIV al documento.
5. Animar el elemento DIV para que se muestre, después de 3.5 segundos animar el elemento DIV para ocultarlo y descargarlo del DOM (destruirlo)

Iremos construyéndola paso a paso para hacer claro el ejemplo:

<script type="text/javascript">
function showMessage(msg) {
//1.
    var message = $('<div></div>');

//2. Notar que la propiedad top comienza con un valor de -28px para que no se vea el elemento DIV al momento de inyectarlo al DOM.
    message.css({
        'height': '20px',
        'width': '100%',
        'position': 'fixed',
        'top': '-28px',
        'left': '0px',
        'margin': '0px',
        'background-color': '#264182',
        'color': '#FFFFFF',
        'font-size': '15px',
        'text-align': 'center',
        'padding-top': '4px',
        'padding-bottom': '4px',
        'font-family': 'Verdana'
    });

//3.
    message.text(msg);

//4.
    $(document.body).append(message);

//5.
    message.animate(
        { 'top': '0px' },
        'normal',
        null,
        function() {
            window.setTimeout(function() {
                message.animate({ 'top': '-28px' }, 'normal', null, function() { message.remove(); });
            }, 3500);
        }
    );
</script>

Es probable que el punto 5 resulte un tanto confuso debido a su redacción, pero no es tan complejo como parece:

1. El primer parámetro de la función animate nos permite determinar las propiedades del estilo que queremos modificar
2. El segundo parámetro dicta la velocidad de la animación.
3. El tercer parámetro es el nombre de la función que determina el tipo de animación a aplicar (funciones matemáticas que calculan cómo se moverá el objeto, jquery tiene la animación linear por defecto).
4. El cuarto parámetro es la función que se ejecutará una vez que la animación haya finalizado, a este parámetro se le llama callback.

En el callback de la animación estamos agregando una función que se ejecutará en 3500 milisegundos.

Se podrá notar que volvemos a ocupar la función animate para ocultar el elemento DIV y su callback destruye el objeto message.

En la siguiente entrada le agregaremos algunas opciones a nuestra función para hacerla mucho más llamativa.

miércoles, 14 de marzo de 2012

MultiUpload dinámico



Hay ocasiones en las que no sabemos de forma exacta cuántos archivos va a cargar el usuario a nuestra aplicación web, por ejemplo una galería fotográfica, donde le damos la posibilidad de cargar 1 o más archivos.

El dolor de cabeza radica en "1 o más" dado que lo que menos queremos es que el usuario tenga que estar dando clic en un botón "Agregar" para que se haga un POST al servidor y generemos otro control de tipo FileUpload y así pueda cargar más fotografías.

En esta entrada explicaré de una forma sencilla y sumamente básica cómo podemos generar controles FileUpload del lado del cliente y así evitemos dar viajes sin sentido al servidor y por lo tanto mejorar la experiencia de nuestro usuario.

La herramienta más sencilla (y versátil) que he encontrado para poder modificar la página desde el navegador del cliente es jquery, por lo que agregaremos el siguiente elemento script en el head de nuestra página Default.aspx (suponiendo que ya cuentan con él y lo han colocado en la carpeta js de nuestro proyecto Web):

<script language="javascript" type="text/javascript" src="js/jquery.js"></script>

Y colocaremos la siguiente interfaz:

<table>
    <tr>
        <td colspan="2"><input type="button" value="Add" onclick="return addUploadField()" /></td>
    </tr>
    <tr>
        <td><input type="file" name="file0" /></td>
        <td></td>
    </tr>
    <tr id="trUploadRow">
        <td colspan="2" align="right"><asp:Button ID="cmdUpload" runat="server" Text="Upload" OnClick="cmdUpload_Click" /></td>
    </tr>
</table>

Tenemos un botón "Add" que nos servirá para agregar un nuevo control de carga de archivos, en las filas siguientes se agregarán los controles de carga de archivos junto con un botón para eliminar el control (si es que el usuario ya no quiere cargar ese archivo) y un botón ASP .NET que ejecutará el POST de la página para la carga de los archivos.

Notemos que es muy importante que nuestros controles de carga de archivos (input de tipo file) deben de contar con el atributo "name" para que éstos sean reconocidos por la propiedad Files del objeto Request.

Ahora escribiremos la función javascript addUploadField que será la encargada de crear los controles de carga:

<script language="javascript" type="text/javascript">
    var currentIndex = 0;
    function addUploadField() {
        currentIndex++;
        var newRow = '<tr>';
        newRow += '<td><input type="file" name="file' + currentIndex + '" /></td>';
        newRow += '<td><input type="button" value="Remove" onclick="return removeUploadField(this)" /></td>';
        newRow += '</tr>';
        newRow = $(newRow);
        newRow.insertBefore($('#trUploadRow'));
    }
    function removeUploadField(e) {
        $(e.parentNode.parentNode).remove();
    }
</script>

La variable currentIndex me ayudará a darle un nombre único a cada control que se agregue.   En la función addUploadField estoy declarando una variable newRow donde estaré concatenando el código HTML necesario para crear el control de carga y el botón de eliminar.

Al atributo name le estoy asignando un valor único al concatenar la variable currentIndex que se incrementa cada vez que el usuario agregue un nuevo control.

En el botón "Remove" estoy llamando a una función "removeUploadField" que será la encargada de eliminar la fila.

Una vez que tengo el código HTML necesario para representar a mi control de carga y mi botón de eliminar, convertiremos el código en objetos jquery para poder manipularos de una forma mucho más simple.    Lo interesante del objeto jquery es que utilizando su funcion insertBefore lo podemos colocar justo arriba del botón "Upload".

En la función removeUploadField únicamente recibimos como parámetro el botón que está llamando a la función, y dado que HTML es un árbol de nodos, podemos obtener a través de la propiedad "parentNode" al objeto TR que queremos eliminar.

Ahora vayamos al archivo Default.aspx.cs donde escribiremos el código de nuestro manejador del evento Click del botón Upload:

using System.IO;
protected void cmdUpload_Click(object sender, EventArgs e)
{
    foreach (string file in Request.Files)
    {
        if (Request.Files[file].ContentLength > 0)
        {
            if (File.Exists(Server.MapPath("~/files/" + Request.Files[file].FileName)))
                File.Delete(Server.MapPath("~/files/" + Request.Files[file].FileName));
            Request.Files[file].SaveAs(Server.MapPath(("~/files/" + Request.Files[file].FileName)));
        }
    }
}

Si ejecutamos la aplicación veremos que aún cuando la estructura de los objetos necesarios para llevar a cabo la carga de los archivos es la adecuada, éstos no son recibidos de forma correcta.    Pongamos un punto de interrupción en la línea del "foreach" y exploremos la propiedad "ContentType" del objeto Request, veremos que tiene el valor "application/x-www-form-urlencoded" y para que los archivos sean transferidos de forma correcta el ContentType debe ser "multipart/form-data".

Se me ocurren dos maneras de corregir este problema:

1. Modificando la etiqueta form de Default.aspx agregándole el atributo "enctype" con el valor "multipart/form-data" de manera tal que quede así:
<form id="form1" runat="server" enctype="multipart/form-data">

2. En el procedimiento Page_Load verificar el valor de la propiedad ContentType del objeto Request y corregirlo si es necesario:

protected void Page_Load(object sender, EventArgs e)
{
    if (form1.Enctype != "multipart/form-data")
        form1.Enctype = "multipart/form-data";
}

Ocupen la que gusten, las dos opciones resultan en la carga correcta de archivos.

Espero haya sido de mucha utilidad esta entrada y les ayude a arrancar con controles propios y extender su funcionalidad tanto como ustedes gusten.