Archivo de la categoría: Javascript

Implementación teórica con BlockChain para un sistema de votaciones

Ha inicios de este año recibí un mensaje en Linkedin donde me preguntaban si estaba interesado en comenzar un proceso de reclutamiento, lo cual no es nada raro ya que siendo ingeniero de software (una de las industrias con más de profesionales en la actualidad) te llegan correos con propuestas laborales todos los días, sin embargo esta propuesta me pareció bastante interesante ya que necesitaban un Senior BlockChain Engineer, el mensaje en cuestión era:

Si bien no tengo experiencia desarrollando BlockChain como tal, si tengo bastante experiencia con sistemas distribuidos, así que me di a la tarea de comenzar a aprender el funcionamiento de esta tecnología desde un punto de vista técnico, sus conceptos básicos y sobre todo tratar de entender por que se volvió tan popular y sera el “futuro”.

Disclaimer: El código mostrado en el articulo a continuación no es una implementación real de BlockChain (no esta ni cerca del 1% de ser un proyecto real) la intención es resaltar y explicar las partes mas importantes y hacer hincapié en los conceptos básicos de lo que es BlockChain, el código dista de ser perfecto y no debe ser usado en producción.

Bueno ya estan advertidos hehe, comenzamos.

El bloque

Para entender lo que es la cadena de bloques primero tenemos que entender lo que es un bloque, el bloque puede ser representado como un objeto que contiene un identificador único, un timestamp, información en cualquier formato que queramos, un hash o checksum que representa la información que contiene y lo mas importante es que debe tener una referencia al bloque anterior, como si de una lista enlazada se tratara.

Con un lenguaje tan sencillo como Javascript podemos representar al bloque de la siguiente forma:

class Block {
  constructor(index, timestamp, data, previousHash = '') {
    this.index = index;
    this.timestamp = timestamp;
    this.data = data;
    this.previousHash = previousHash;
    this.hash = this.calculateHash();
  }

  calculateHash() {
    return SHA256(this.index + this.timestamp + JSON.stringify(this.data) + this.previousHash).toString();
  }
}

La clase bloque contiene una función muy especial llamada calculateHash, esta función toma toda la información del bloque (index, timestamp, data, previousHash), la concatena y le aplica un algoritmo de hashing para generar un checkSum que despues sera almacenado en el mismo bloque, la parte interesante y una de las razones por las que BlockChain es seguro es precisamente el uso de estas funciones criptográficas, ya que su integridad esta respaldada de forma matemáticamente.

Detengamonos por unos segundos y pensemos, si todo bloque debe de tener una referencia al hash del bloque anterior, y ese dato (previousHash) se utiliza para calcular el hash del bloque actual eso significa que si alguien hackea/modifica/elimina uno de los bloques anteriores todos los siguientes bloques quedarían invalidados 🙂 , seria un efecto domino de fallas en la integridad de los bloques.

Esta es la representación mas básica que se me ocurre de un bloque, en la vida real son estructuras de datos mucho mas complejas y en la parte de los datos pueden contener muchísimas mas piezas de información y no solo una, como lo es el caso de las transacciones en la BlockChain de Bitcoin.

La cadena

Habiendo entendido un poco lo que es el bloque ahora toca el turno de la cadena, esta es otra estructura de datos que funciona alrededor de los bloques y realiza operaciones con ellos, mencione que esta tecnología es muy parecida a una lista enlazada, si el bloque es uno de los nodos entonces la cadena serian las operaciones de agregar, eliminar, modificar, etc nodos, aunque en BlockChain solo podemos agregar bloques y nunca eliminarlos por lo que mencionaba sobre la integridad de los hashes.

En Javascript podemos representar la cadena de la siguiente forma:

class BlockChain {
  constructor() {
    this.chain = [this.createGenesisBlock()];
  }

  createGenesisBlock() {
    return new Block(0, new Date(), "Genesis block");
  }

  getLatestBlock() {
    return this.chain[this.chain.length - 1];
  }

  addBlock(block) {
    block.previousHash = this.getLatestBlock().hash;
    block.hash = block.calculateHash();
    this.chain.push(block);
  }

  isChainValid() {
    for(let i = 1; i < this.chain.length; i++) {
      const currentBlock = this.chain[i];
      const previousBlock = this.chain[i - 1];
      if (currentBlock.hash !== currentBlock.calculateHash()) {
        return false;
      }
      if(currentBlock.previousHash !== previousBlock.hash) {
        return false;
      } 
    }
    return true;
  }
}

Tenemos varias funciones interesantes como addBlock (agregar nuevo bloque), getLatestBlock (obtener el ultimo bloque), isChainValid (valida la integridad de la cadena de bloques) y createGenesisBlock, estas dos ultimas son las mas interesantes.

Mencione que cada bloque debe tener una referencia al bloque anterior ¿Pero entonces cuál fue el primer bloque 🙂 ?

Toda cadena de bloques inicia con un bloque llamado Bloque Genesis, ese nombre no es nada mas que una convención, es la forma de inicializar la cadena.

La función isChainValid verifica la integridad de la cadena utilizando los hashes de cada uno de los bloques, comienza revisando a partir del segundo bloque (uno después del bloque génesis) y primero revisa que el hash actual efectivamente corresponda con la información del bloque

if (currentBlock.hash !== currentBlock.calculateHash()) {
  return false;
}

Después revisa que la referencia (previousHash) al bloque anterior sea la correcta:

if(currentBlock.previousHash !== previousBlock.hash) {
  return false;
} 

Así hasta llegar al ultimo bloque y si todo sale bien la cadena es valida :). Sin embargo si entendieron bien el concepto de verificación de integridad pueden ver el problema con esta implementación, si bien no es posible modificar la información de un bloque intermedio, teóricamente es posible “hackear” la información del ultimo bloque antes de que agreguen nuevos nodos a la cadena.

Este precisamente es el problema que se aborda en el primer paper de Bitcoin por Satoshi Nakamoto: Bitcoin: A Peer-to-Peer Electronic Cash System

Sin entrar en detalles, en implementaciones reales la cadena de bloques no se encuentra centralizada en una sola maquina, en lugar de eso es una red distribuida y todas las computadoras de la red contienen una copia completa de la cadena, entonces cada vez que alguien va a agregar un nuevo bloque toda la red tiene que consentir y finalmente ese bloque se almacena en las cadenas de todas las maquinas, sin embargo esta solución abre la posibilidad a nuevos tipos de ataques como por ejemplo el double spend attack, aun así, teóricamente sigue siendo posible “hackear” la BlockChain pero para eso tendrías que modificar los últimos bloques de todas las maquinas de la red lo cual requiere una cantidad inmensa de recursos y por lo tanto no es factible.

Ejemplo de BlockChain para votaciones

Habiendo aprendido los conceptos básicos ahora podemos pensar en como usar BlockChain para almacenar información referente a un sistema de votaciones, vamos a escribir unas cuantas pruebas en Javascript para probar nuestras dos clases, Block y BlockChain.

  it('Generate genesis block', () => {
    voteChain = new BlockChain();
    assert.lengthOf(voteChain.chain, 1);
    assert.equal(voteChain.chain[0].data, 'Genesis block');
  });

Con este test probamos que la cadena se inicializa y el bloque génesis es generado correctamente (la longitud de la cadena es 1 y data del primer bloque contiene el string ‘Genesis block’)

Ahora vamos a agregar unos cuantos bloques a la cadena

  it('Add some blocks', () => {
    voteChain.addBlock(new Block(1, new Date(), { user: 'Manuel', voted: 'PRI' }));
    voteChain.addBlock(new Block(2, new Date(), { user: 'Andres', voted: 'PAN' }));
    voteChain.addBlock(new Block(3, new Date(), { user: 'Julio', voted: 'PRD' }));
    voteChain.addBlock(new Block(4, new Date(), { user: 'Carlos', voted: 'PRI' }));
    voteChain.addBlock(new Block(5, new Date(), { user: 'Ruben', voted: 'PAN' }));
    voteChain.addBlock(new Block(6, new Date(), { user: 'Laura', voted: 'PRD' }));
    assert.lengthOf(voteChain.chain, 7);
  });

Estamos almacenando nombres de personas y el partido político mexicano por el que votaron, un bloque a la vez y al final verificamos que la longitud de la cadena es 7 (incluyendo el bloque génesis). En la vida real esto es mucho mas complicado ya que la gente no podría agregar bloques “solo por que si”, al igual que con Bitcoin se utilizaría una tecnología de PKI (Infraestructura de llave publica) en donde cada transacción de voto deberá ser firmada usando la llave privada (como con la FIEL del SAT) de la persona que emite su voto, de esa manera nadie podría votar a nombre de alguien mas.

Ahora tenemos nuestro test para verificar la integridad de la BlockChain, si la cadena es valida esperaríamos que la función isChainValid nos devolviera true.

  it('Validate chain integrity', () => {
    assert.isOk(voteChain.isChainValid(), 'Block hashes are incorrect'); // this should return true
  });

Observen como en cada uno de los bloques el valor de previousHash es identico al hash del bloque anterior

En la ultima prueba simulamos que alguien modifico el voto de alguien mas, para eso tomamos de forma aleatoria un bloque y cambiamos el valor de su data por el nombre de otro partido político.

  it('Changing random data in the BlockChain', () => {
    const min = 0; // first block id
    const max = 5; // for academic purpose this cannot be 6 (the last block id) due to double spend attack
    const blockId = Math.floor(Math.random() * (max - min + 1) + min); // https://stackoverflow.com/questions/4959975/generate-random-number-between-two-numbers-in-javascript
    const block = voteChain.chain[blockId];
    block.data = { user: block.data.user, voted: 'MORENA' }; // Changing block value
    block.hash = block.calculateHash(); // Re calculate the current block hash so no one notices the hack
    assert.isNotOk(voteChain.isChainValid(), 'Block hashes are incorrect'); // this should return false
  });

El test, de forma aleatoria, tomo el bloque con index 2 y cambio los valores que tenia en data y eso hizo que el hash resultante de ese bloque cambiara completamente.

hash antes del cambio:
9ecbe54e3e6b3f22a522bbee7f399002f1de2d653c15a74d2d321f27cdfe116b

hash después del cambio:
3e6c8f5c0a734db4bb19483a11022081250171621e5c5c17c21a302d1d9d14d0

Por lo tanto el previousHash del bloque index 3 ya no coincide con el del bloque index 2.

Debido a este cambio la validación de integridad de la cadena fallara (siempre y cuando no modifiquemos los datos del ultimo bloque) y isChainValid() nos devolverá false.

Y eso es todo por ahora, finalmente si quieren descargar este sencillo código para verlo mas a detalle o simplemente para tenerlo lo pueden hacer desde el siguiente repositorio de github alevsk blockchain javascript con los siguientes comandos

$ git clone https://github.com/Alevsk/blockchain-javascript
$ cd blockchain-javascript
$ npm install
$ mocha index.test.js

Resumen

BlockChain nos ofrece ante todo integridad de la información, nos asegura que la información no puede ser cambiada ni eliminada sin que nos demos cuenta, ademas por su naturaleza de ser un sistema distribuido es virtualmente imposible hackear la red, esta tecnología aplicada a un sistema de votaciones nos garantiza que algo como el robo de votos no puede ocurrir y tendríamos total transparencia en las votaciones.

Si quieren aprender mas sobre esta tecnología les recomiendo ver este video en donde se explica con mucho mas detalle como funciona Bitcoin y la BlockChain en general, se abordan muchísimos mas conceptos de los que mencione en este articulo.

Happy hacking 🙂

Introducción a GraphQL, Queries y Mutations

Como algunos de ustedes sabrán llevo poco mas de 1 año trabajando con una startup (si, deje Oracle XD) cuyo stack esta conformado en su mayoría por tecnologías de Javascript (NodeJS, ReactJS, Redux, Apollo, GraphQL, React-native, etc). y en esta ocasión quiero compartir con ustedes el material de la platica que di en el GDLJS del mes de octubre en Guadalajara, se trata de una breve introducción a GraphQL y cual ha sido mi experiencia con esta tecnología.

¿Que es GraphQL?

Primero lo primero, GraphQL es un lenguaje de consultas para tu API creado por Facebook en 2012, es decir, es un intermediario comúnmente utilizado entre un cliente y algún orm de tu elección, es importante mencionar que GraphQL no se conecta directamente a tu base de datos, en lugar de eso ayuda a que el cliente defina el formato de la respuesta que desea obtener del servidor, mas adelante veremos algunos ejemplos.

¿Cual es la diferencia?

Ya existen bastantes frameworks para desarrollar apis ¿Por que quisiera usar GraphQL?

Bueno una de las principales diferencias con apis basadas en REST simple que tienen múltiples endpoints es que en tu api basada en GraphQL solo tendras uno.

Ademas de eso las apis comunes utilizan varios métodos HTTP (GET, POST, DELETE, PUT, OPTIONS, etc) según la operación que vayan a realizar, mientras que con GraphQL usaras solamente POST si así lo deseas, un endpoint para gobernarlos a todos 😉

Todo bien hasta aquí, pero no me haz dicho realmente cual es el beneficio de usar esta tecnología

Tranquilo pequeño saltamontes, consideremos el caso siguiente:

Del lado izquierdo tenemos un cliente que hace una petición GET a un endpoint de álbumes pasando un id para obtener sus assets, posteriormente por cada uno de esos assets solicita los comentarios (múltiples peticiones al servidor), adicionalmente los objetos JSON que reciba en las respuestas siempre tendrán los mismos atributos.

Del lado derecho vemos la petición POST equivalente para un endpoint basado en GraphQL, como podemos observar en el mismo payload de nuestra petición estamos indicando el formato de respuesta que queremos que el servidor nos regrese, atributos en los objetos, etc.

Habra quien diga que puede ingeniárselas para que la petición defina la respuesta del servidor, regresar atributos dinámicamente, etc. y le creo pero buena suerte manteniendo algo como esto 🙂

GET /albums/1/assets/comments/?include=asset.name,comment.author,comment.text

Este es precisamente el problema que GraphQL resuelve, GraphQL nos permite definir relaciones entre las entidades de nuestra aplicación e inyectar esos objetos relacionados en las respuestas cada vez que el cliente lo pida.

El siguiente ejemplo de código esta basado en Javascript utilizando expressJS, supongamos que el cliente necesita desplegar en su frontend un objeto como el siguiente:

Un objeto película con datos como su nombre, el año y la calificación de la critica, adicionalmente también queremos los datos de los actores involucrados y los comentarios de los visitantes que han visto esa pelicula.

Manos a la obra, vamos a iniciar un nuevo proyecto con NodeJS

$ npm init
$ npm install --save express express-graphql graphiql graphql

Adicionalmente me gusta definir algunos comandos e instalar algunas dependencias para tener soporte es6, aquí pueden ver como queda mi package.json al final.

La estructura del proyecto es mas o menos la siguiente (demo-server)

Los archivos y carpetas mas importantes son:

  • app.js es nuestro entry point
  • graphql es la carpeta donde guardaremos nuestros “objetos QL”
  • data es la carpeta donde tendremos algunos objetos de ejemplo que simulan registros de la base de datos

Vamos a comenzar con el objeto Movie (película), en la carpeta graphql creamos un nuevo archivo llamado movieQL.js

import {
  GraphQLObjectType,
  GraphQLInt,
  GraphQLString,
  GraphQLList,
  GraphQLBoolean,
  GraphQLNonNull,
  GraphQLFloat,
} from 'graphql';

import actorQL from './actorQL';
import commentQL from './commentQL';

const movieQL = new GraphQLObjectType({
  name: 'movieQL',
  description: 'This is a movie QL object',
  fields: () => {
    return {
      name: {
        type: GraphQLString,
        resolve(movie) {
          return movie.name;
        }
      },
      score: {
        type: GraphQLFloat,
        resolve(movie) {
          return movie.score;
        }
      },
      year: {
        type: GraphQLInt,
        resolve(movie) {
          return movie.year;
        }
      },
      actors: {
        type: new GraphQLList(actorQL),
        resolve(movie) {
          return movie.actors;
        }
      },
      comments: {
        type: new GraphQLList(commentQL),
        resolve(movie) {
          return movie.comments;
        }
      },
    }
  }
});

export default movieQL;

Como podemos observar al inicio estamos haciendo import de varios módulos que representan tipos de datos escalares en graphQL, adicionalmente hacemos import de otras 2 entidades de nuestra aplicación, actorQL.js y commentQL.js, después en el atributo fields de nuestro objeto movieQL definimos varios campos del mismo junto con su tipo y aqui viene lo mas importante, definimos actors como una lista de tipo actorQL y comments como una lista de tipo commentQL, el código de las otras entidades es bastante similar al de movieQL por lo que no lo pondre en el post, pueden revisarlo en el repositorio: actorQL.js y commentQL.js

Queries y mutations

Otro de los conceptos básicos en graphQL son las queries y las mutations, existe toda una teoría detrás pero en resumen:

  • Queries: nos permiten leer datos del servidor (por lo general extraídos de una db)
  • Mutations: Crear / modificar / borrar datos en el servidor

Dentro de la misma carpeta graphql vamos a crear 2 nuevos archivos, queryQL.js y mutationQL.js

import {
  GraphQLObjectType,
  GraphQLList,
  GraphQLString,
  GraphQLInt,
  GraphQLBoolean
} from 'graphql';

import movieQL from './movieQL';
import { movies } from '../data';

const query = new GraphQLObjectType({
    name: 'Query',
    description: 'This is the root Query',
    fields: () => {
      return Object.assign({
        getMovies: {
          type: new GraphQLList(movieQL),
          args: {},
          resolve(root, args, request) {
            // do some db queries
            return movies;
          }
        },
      });
    },
});

export default query;

Para efectos de que esto es un demo no estamos utilizando ningún orm para conectarnos a alguna base de datos, pero ustedes son libres de elegir e implementar el que mas le guste, de la misma forma que en movieQL.js definimos los fields aquí estamos definiendo nuestros “endpoints”, por ejemplo estamos diciendo que getMovies es una query que nos regresara una lista de movieQL y estamos haciendo return del objeto movies (que es un objeto de ejemplo que importamos de la carpeta data).

De la misma forma dentro de mutationQL.js declaramos una operación llamada createMovie que nos retornara un objeto tipo movieQL (después de haberlo creado), la parte importante aquí es que por lo general los mutations reciben argumentos (name, year, score, lista de actores, lista de comentarios) y de nuevo, para efectos de que esto es un demo no estamos haciendo nada con los datos que nos enviá el usuario, simplemente los regresamos en la respuesta.

import {
  GraphQLObjectType,
  GraphQLInt,
  GraphQLString,
  GraphQLNonNull,
  GraphQLList,
  GraphQLInputObjectType,
  GraphQLBoolean,
  GraphQLFloat,
} from 'graphql';

import movieQL from './movieQL';

const actorInputQL = new GraphQLInputObjectType({
  name: 'actorInputQL',
  fields: {
    name: { type: GraphQLString },
    age: { type: GraphQLInt },
    country: { type: GraphQLString },
  },
});

const commentInputQL = new GraphQLInputObjectType({
  name: 'commentInputQL',
  fields: {
    user: { type: GraphQLString },
    commentary: { type: GraphQLString },
    timestamp: { type: GraphQLString },
  },
});

const mutation = new GraphQLObjectType({
  name: 'Mutation',
  description: 'This is the root Mutation',
  fields: () => {
    return Object.assign({
      createMovie: {
        type: movieQL,
        args: {
          name: {
            type: new GraphQLNonNull(GraphQLString),
          },
          year: {
            type: GraphQLInt,
          },
          score: {
            type: GraphQLFloat,
          },
          actors: {
            type: new GraphQLList(actorInputQL),
          },
          comments: {
            type: new GraphQLList(commentInputQL),
          },
        },
        resolve(root, args, request) {
          // do something here
          return args;
        },
      },
    });
  },
});

export default mutation;

Hasta aquí ya tenemos definidos nuestros queries y mutations de ejemplo, ha llegado el momento de definir un schema de graphQL e integrar todo con express, es bastante sencillo, comenzamos creando un archivo llamado schemaQL.js también dentro de la carpeta graphql

import { GraphQLSchema } from 'graphql';
import queryQL from './queryQL';
import mutationQL from './mutationQL';

const schemaQL = new GraphQLSchema({
  query: queryQL,
  mutation: mutationQL,
});

export default schemaQL;

Como podemos ver, simplemente importamos los modulos de queryQL y mutationQL y finalmente en nuestro entry point (app.js) mandamos llamar a graphQL con el schema recién creado.

import express from 'express';
import GraphHTTP from 'express-graphql';
import schemaQL from './graphql/schemaQL';

var app = express();

app.use('/graphiql', GraphHTTP({
    schema: schemaQL,
    pretty: true,
    graphiql: true
}));

app.use('/graphql', GraphHTTP({
    schema: schemaQL
}));

app.get('/', function (req, res, next) {
  const reponse = {
    message: 'hello world',
  };
  return res.json(reponse);
});

module.exports = app;

Notaran que tenemos definidos 2 endpoints, graphql y graphiql. GraphiQL es una herramienta bastante útil que viene con el modulo de graphQL, se trata de una pequeña interfaz web desde donde podemos probar nuestras queries y mutations y la cual nos genera una documentación con base en los objetos QL de nuestro código, por ejemplo para probar nuestra query de getMovies seria algo como lo siguiente:

Observen que del lado izquierdo estoy definiendo los atributos que quiero que contengan los objetos de la respuesta, puedo solicitar mas o menos dependiendo de lo que el cliente pida, ayudando bastante a, por ejemplo, reducir el tamaño de los mensajes si la petición se hace desde un cliente móvil.

De la misma forma podemos probar nuestro mutation por medio de graphiQL

Observen como desde el cliente podemos pasar directamente el objeto con sus atributos, incluso los objetos relacionados como la lista de actores y comentarios, ya es cuestión de procesar todo eso en nuestro backend y crear los registros en la base de datos.

Todo bien hasta el momento, ya sabemos utilizar graphiQL, ahora como usamos nuestra api ya en un proyecto real, muy sencillo, cada vez que hacemos un request en google developer toolbar podemos observar cual es el payload que se enviá al servidor:

Podemos tomar ese mismo payload y con la ayuda de POSTMAN enviarlo como raw body a nuestro endpoint de graphQL en /graphql

Observa como el POST request va dirigido a /graphql y no /graphiql, por ultimo desde el mismo POSTMAN podemos ver cual seria el HTTP request generado haciendo clic en el boton code

Finalmente lo único que queda es implementar ese request en tu lenguaje de programación / framework favorito, a continuación dejo la presentación que utilice durante el evento por si necesitan revisarla asi como el repositorio de github donde esta alojado el código de este demo: graphql demo server

50% de descuento para Campus Party 2014!

cover2

Uno de los eventos más importantes de Tecnología y ambiente tecnológico del mundo se celebrará en nuestro país en Zapopan, hablamos de CPMX5 y hoy te mostraremos la oportunidad de como obtener tus entradas con el 50 de descuento.

  • Deben registrarse como campusero en www.campus-party.com.mx.
  • Si ya tienes usuario solo debes hacer login.
  • Una vez hecho login en el panel de usuario deben seleccionar la opción «Comprar entrada». (http://www.campus-party.com.mx/webapp/fichaUsuario/pago)
  • Elegir la opción «Comprar con descuento».
  • En el proceso deben usar el siguiente código de descuento: CD501ihw35hi (copia y pega).
  • Una vez elegido el tipo de entrada se procede al pago de la cantidad vía PayPal.

¡Y listo! solo faltaría esperar a que llegue el día de comienzo de #CPMX5 para disfrutar de una semana de contenidos para los fans de la tecnología y lo que esto implica.

Comentario tipo facebook utilizando jQuery

Muchas veces mientras desarrollamos una aplicación web nos vemos en la necesidad de implementar algo que permita a los usuarios involucrarse en lo que sea que estemos haciendo, los sistemas de comentarios son una parte muy común en toda plataforma web de hoy en día, se trata de una de las características principales que no pueden faltar en ningún sitio, blog o red de noticias, básicamente un sistema de comentarios abre un canal de comunicación entre nosotros y los visitantes. Teniendo esto en cuenta no está de más mencionar que existen muchos servicios gratuitos en la red que nos permiten implementar un sistema de comentarios de forma muy rápida como por ejemplo Disqus.

En principio lo mínimo que tiene que tener un sistema de comentarios es un nombre de quien comenta, avatar y texto del comentario, entonces siguiendo este principio básico hoy vamos a implementar una interfaz de sistema de comentarios tipo Facebook utilizando HTML y jQuery. Los comentarios en Facebook tienen una característica muy particular, ha diferencia de muchos otros, Facebook no recarga la página una vez que escribimos y presionamos enter, esto es posible gracias al uso de Ajax, a grandes rasgos Ajax es una tecnología que permite que nuestro navegador (Chrome, Firefox, safari, etc) pueda mandar petición al servidor de manera asíncrona, dándonos después la posibilidad de modificar ciertas secciones de nuestra pagina usando JavaScript.

Echemos un vistazo a como lucen los comentarios en Facebook, después podremos comenzar a desarrollar nuestra copia (y aprender de ello en el proceso :))

facebook_comments

Como mencionaba más arriba, básicamente se compone de, un cuadro de texto para escribir el comentario, y cada comentario tiene nombre, avatar y texto, pues manos a la obra.

Paso 1: Crear la estructura en HTML

Usando HTML vamos a comenzar a crear lo que será nuestro formulario y una lista de comentarios de ejemplo, para esto utilizaremos divs.
El formulario para escribir comentarios puede ser muy sencillo y constar solo de 2 campos, el texto y el nombre de usuario:

<form action="post.php" id="postComment" method="post" accept-charset="utf-8">
   <input type="text" name="newComment" required="" autocomplete="off" placeholder="¿En que estas pensando?" id="newComment"></input>
   <input type="hidden" name="username" id="username" value="Alevsk"></input>
</form>

Y nuestro “modelo” de comentario estará conformado solamente por una imagen y un espacio que mostrara texto, algo como:

<div class="comment">
     <div class="avatar">
         <img src="img/1.png" />
     </div>
     <div class="autoComment">
         <span>Alevsk </span>
         Comentario de ejemplo	    	
     </div>
</div>

Ok, entonces ahora combinando ambas cosas el código completo de nuestro index.html se tendría que ver más o menos así.

<!DOCTYPE html>
	<head>
  		<meta charset="utf-8" />
  		<meta name="viewport" content="width=device-width" />
    	<title>Comentarios tipo facebook con jQuery</title>
    	<script type="text/javascript" src="jquery.js"></script>
    	<script type="text/javascript" src="jquery.validate.js"></script>
	</head>
  	<body>
  		<div id="container">
	  		<div id="comments">
	  			<div class="comment">
					<div class="avatar">
						<img src="img/1.png" />
					</div>

				    <div class="autoComment">
				    	<span>Alevsk </span>
				    	Comentario de ejemplo	    	
			        </div>
	  			</div>
	  			<div class="comment">
					<div class="avatar">
						<img src="img/2.png" />
					</div>

				    <div class="autoComment">
				    	<span>Chell </span>
				    	Comentario de ejemplo	    	
			        </div>
	  			</div>
	  			<div class="comment">
					<div class="avatar">
						<img src="img/3.png" />
					</div>

				    <div class="autoComment">
				    	<span>Calorine </span>
				    	Comentario de ejemplo	    	
			        </div>
	  			</div>
	  		</div>
	  		<div id="commentBox">
	    		<form action="post.php" id="postComment" method="post" accept-charset="utf-8">
	  				<input type="text" name="newComment" required="" autocomplete="off" placeholder="¿En que estas pensando?" id="newComment"></input>
	  				<input type="hidden" name="username" id="username" value="Alevsk"></input>
	  			</form>
	  		</div>
  		</div>
  	</body>
</html>

Notaran que el Head he agregado de una vez la librería de jQuery y un plugin de validación, estos los necesitaremos más adelante ya que nos facilitaran bastante el trabajo :), con el código anterior llevamos algo como la siguiente imagen, ¿no se ve muy lindo verdad?, eso es porque nos falta agregarle un poco de estilos.

facebook_comments_1

Paso 2: Agregar estilos

Agregamos el siguiente código CSS en cualquier lugar dentro de las etiquetas de head (lo correcto sería tener el css en un archivo aparte, pero para fines demostrativos está bien así por ahora).

    	<style type="text/css">
    		body
    		{
    			font-family: 'lucida grande',tahoma,verdana,arial,sans-serif;
				font-size: 11px;
    		}
    		#container
    		{
				width: 290px;
				margin: 0 auto;
				height: auto;
				overflow: auto !important;
    		}
    		#commentBox
    		{
				background: #edeff4;
				height: 25px;
				overflow: hidden;
				display: block;
				padding: 10px;
    		}
	    	#postComment input
	    	{
				width: 100%;
	    	}
	    	#postComment .enviar
	    	{
	    		clear: both;
				float: left;
	    	}

	    	#comments
	    	{
				margin-top: 15px;
				float: left;
				width: 100%;
				background: #edeff4;
				overflow: auto;
	    	}
	    	#comments .comment
	    	{
				padding: 10px;
				border-bottom: 1px solid #fff;
				overflow: auto;
	    	}
	    	#comments .comment .avatar
	    	{
				float: left;
				margin-right: 10px;
	    	}
	    	#comments .comment .avatar img
	    	{
	    		width: 32px;
	    		height: 32px;
	    	}
	    	#comments .comment .autoComment
	    	{
				float: right;
				width: 225px;
	    	}
	    	#comments .comment .autoComment span
	    	{
	    		font-weight: bold;
	    		color: #3b5998;
	    		cursor:pointer;
	    	}
    	</style>

Recargamos la página y como por arte de magia esto se va pareciendo un poco más a los comentarios de facebook

facebook_comments_2

ijCBM2h1of0vE

En este punto tenemos algo ya muy bonito, lamentablemente no hace nada!, pero no nos desesperemos ya que solo nos falta una cosa :).

Paso 3: Utilizar jQuery para agregar los nuevos comentarios

Lo que tendremos que hacer ahora será que cuando el usuario escriba su comentario y pulse la tecla enter este sea enviado de manera asíncrona (utilizando Ajax) al servidor, después tendremos que dibujar en la pantalla de alguna manera lo que el visitante acaba de comentar, todo esto sin recargar la página. Esto puede sonar más complicado de lo que es, sobre todo si eres nuevo con jQuery. Por suerte jQuery nos permite manipular el DOM, podemos incluir elementos diatónicamente en nuestro sitio de manera muy sencilla.

¿Recuerdan el plugin de validación que había agregado en un inicio?, bueno pues ahora viene la parte interesante, el plugin nos permite agregar validaciones de todo tipo en los campos y capturar ciertos eventos que ocurren en un formulario, por ahora utilizaremos uno llamado submitHandler. submitHandler nos permite indicar código para ser ejecutado una ves que el formulario ha activa el disparador submit, esto es especialmente util a la hora de validar campos y mostrar información en la pantalla. De nuevo, este código va en cualquier parte de nuestro index.html pero lo recomendable es que este colocado entre las etiquetas head y después de haber includio la libreria jQuery y el plugin de validation.

		<script type="text/javascript">
			$(document).ready(function(){
				$('#postComment').validate({
					submitHandler: function(form) {
		    			
		  			},
			        errorPlacement: function(error,element) {
			          return;
			        },
		      	});
			});
		</script>

Después el bloque de código que se ejecutara dentro del submitHandler será una petición post al servidor utilizando ajax.

$.ajax({
						      type: "POST",
						      contentType: "application/json; charset=utf-8",
						      url: $(form).attr('action'),
						      data: $(form).serialize(),
						      success: function (result) {

						           var comment = $('<div></div>').addClass('comment');
						           var avatar = $('<div></div>').addClass('avatar');
						           var img = $('<img/>').attr({'src':'img/1.png'});
						           var text = $('<div></div>').addClass('autoComment').html('<span>'+$('#username').val()+' </span>'+$('#newComment').val());

						           avatar.append(img);
						           comment.append(avatar);
						           comment.append(text);

						           $('#comments').append(comment);
						           $('#newComment').val('');
						      }
						 });

Por el momento el formulario está mandando una petición al archivo post.php que no tiene nada, pero sería posible agregar código en este archivo que permitiera guardar los comentarios una base de datos (tal vez muestre como hacer eso en un artículo futuro)

<?php 
	// Guardar el contenido en una base de datos
	// O lo que quieras
	echo "Ok"; 
?>

Ok, continuamos, lo verdaderamente importante aquí es el siguiente código:

						           var comment = $('<div></div>').addClass('comment');
						           var avatar = $('<div></div>').addClass('avatar');
						           var img = $('<img/>').attr({'src':'img/1.png'});
						           var text = $('<div></div>').addClass('autoComment').html('<span>'+$('#username').val()+' </span>'+$('#newComment').val());

						           avatar.append(img);
						           comment.append(avatar);
						           comment.append(text);

						           $('#comments').append(comment);

Como mencionaba más arriba jQuery nos permite manipular el DOM, con las instrucciones anteriores estamos creando los divs necesarios con el texto y el nombre del visitante que después serán agregados a la lista de comentarios existentes, esto nos dará la ilusión de que el comentario fue agregado inmediatamente después de ser escrito (aunque podría ser el caso de que la información no haya terminado de ser procesada en el servidor).

Combinando el HTML, el CSS y el javascript nuestro archivo index.html tendría que verse de la siguiente manera:

<!DOCTYPE html>
	<head>
  		<meta charset="utf-8" />
  		<meta name="viewport" content="width=device-width" />
    	<title>Comentarios tipo facebook con jQuery</title>
    	<script type="text/javascript" src="jquery.js"></script>
    	<script type="text/javascript" src="jquery.validate.js"></script>
		<script type="text/javascript">
			$(document).ready(function(){
				$('#postComment').validate({
					submitHandler: function(form) {
		    			
		    			 $.ajax({
						      type: "POST",
						      contentType: "application/json; charset=utf-8",
						      url: $(form).attr('action'),
						      data: $(form).serialize(),
						      success: function (result) {

						           var comment = $('<div></div>').addClass('comment');
						           var avatar = $('<div></div>').addClass('avatar');
						           var img = $('<img/>').attr({'src':'img/1.png'});
						           var text = $('<div></div>').addClass('autoComment').html('<span>'+$('#username').val()+' </span>'+$('#newComment').val());

						           avatar.append(img);
						           comment.append(avatar);
						           comment.append(text);

						           $('#comments').append(comment);
						           $('#newComment').val('');
						      }
						 });
		  			},
			        errorPlacement: function(error,element) {
			          return;
			        },
		      	});
			});
		</script>
    	<style type="text/css">
    		body
    		{
    			font-family: 'lucida grande',tahoma,verdana,arial,sans-serif;
				font-size: 11px;
    		}
    		#container
    		{
				width: 290px;
				margin: 0 auto;
				height: auto;
				overflow: auto !important;
    		}
    		#commentBox
    		{
				background: #edeff4;
				height: 25px;
				overflow: hidden;
				display: block;
				padding: 10px;
    		}
	    	#postComment input
	    	{
				width: 100%;
	    	}
	    	#postComment .enviar
	    	{
	    		clear: both;
				float: left;
	    	}

	    	#comments
	    	{
				margin-top: 15px;
				float: left;
				width: 100%;
				background: #edeff4;
				overflow: auto;
	    	}
	    	#comments .comment
	    	{
				padding: 10px;
				border-bottom: 1px solid #fff;
				overflow: auto;
	    	}
	    	#comments .comment .avatar
	    	{
				float: left;
				margin-right: 10px;
	    	}
	    	#comments .comment .avatar img
	    	{
	    		width: 32px;
	    		height: 32px;
	    	}
	    	#comments .comment .autoComment
	    	{
				float: right;
				width: 225px;
	    	}
	    	#comments .comment .autoComment span
	    	{
	    		font-weight: bold;
	    		color: #3b5998;
	    		cursor:pointer;
	    	}
    	</style>
	</head>
  	<body>
  		<div id="container">
	  		<div id="comments">
	  			<div class="comment">
					<div class="avatar">
						<img src="img/1.png" />
					</div>
				    <div class="autoComment">
				    	<span>Alevsk </span>
				    	Comentario de ejemplo	    	
			        </div>
	  			</div>
	  			<div class="comment">
					<div class="avatar">
						<img src="img/2.png" />
					</div>

				    <div class="autoComment">
				    	<span>Chell </span>
				    	Comentario de ejemplo	    	
			        </div>
	  			</div>
	  			<div class="comment">
					<div class="avatar">
						<img src="img/3.png" />
					</div>

				    <div class="autoComment">
				    	<span>Calorine </span>
				    	Comentario de ejemplo	    	
			        </div>
	  			</div>
	  		</div>
	  		<div id="commentBox">
	    		<form action="post.php" id="postComment" method="post" accept-charset="utf-8">
	  				<input type="text" name="newComment" required="" autocomplete="off" placeholder="¿En que estas pensando?" id="newComment"></input>
	  				<input type="hidden" name="username" id="username" value="Alevsk"></input>
	  			</form>
	  		</div>
  		</div>
  	</body>
</html>

Bueno con este concluye este corto y básico tutorial, les pueda servir como base a la hora de implementar sus sistemas de comentarios y como siempre en los siguientes enlaces pueden descargar el codigo fuente y ver un ejemplo funcionando. Cualquier duda pueden escribirla en la sección de comentarios xD.

salu2

Lista de ganadores de entradas para Campus Party 2013

Muchas gracias a todos los que participaron el sorteo de 3 entradas gratis para asistir a Campus Party México 2013, las personas listadas a continuación ganaron una entrada con puesto en sala para el evento :), muchas felicidades.

  • Braicot Espinoza
  • Rocio Barragan
  • Jose Vazquez Quezada

Si no resultaste ganador no te desanimes, todavía puedes conseguir tu entrada con un 20% de descuento :)!

¿Como? Muy fácil, tan solo pídela utilizando nuestra sección de contacto y con mucho gusto te enviare un código promocional para que puedas adquirir tu entrada!.

Ya falta poco, nos vemos en Campus Party México 2013

salu2