jueves, agosto 15, 2013

Un Chat con Meteor.js (sí, uno más!)

Hola gente,

vamos a desarrollar un chat utilizando Meteor.js

Soy consciente que existen varios ejemplos sobre esto en la web, éste es uno más.
Me he propuesto realizar un chat minimalista que sirva como base para practicar.
Lo sobresaliente de este ejemplo es el poco código que posee.

Iniciamos el proyecto:

mrt create magmchat
cd magmchat
rm magmchat.*
mkdir client server public


Agregamos los paquetes que utilizaremos:

meteor add bootstrap
meteor add accounts-ui
meteor add accounts-base
meteor add accounts-password


Esquema de la página:

A continuación un esquema simple de como se verá la página, es buena práctica definir estos esquemas antes de comenzar a trabajar, luego se simplifica la tarea.



Implementación del esquema:

Archivo: client/magmchat.html

<head></head>
<body>
  <div class="navbar navbar-static-top">
    <div class="navbar-inner">
      <a class="brand" href="http://blog.magm.com.ar" target="_blank">Magm's Chat</a>
      <ul class="nav navbar-nav pull-right">
        <li><a href="#">{{loginButtons align="right"}}</a></li>
      </ul>
    </div>
  </div>
  {{> inicio}}
</body>

El código anterior define la barra de navegación y la posición de widget {{loginButtons}}, al final se renderizará la plantilla {{> inicio}}, en la cual se definirá el resto de la página.

<template name="inicio">
  {{#if currentUser}}
    <div class="row vspace">
      <div class="offset4 span4 text-center">
        <textarea id="texto" rows="5"></textarea>
      </div>
      <div class="span4">
        Instrucciones:
        <ul>
          <li><span class="label label-success">Enter</span>=enviar texto</li>
          <li><span class="label label-success">Ctrl+Enter</span>=nueva línea</li>
          <li>Puede utilizar código html, por ejemplo: Hola &lt;b&gt;Meteor&lt;/b&gt;</li>
        </ul>
      </div>
    </div>
    <hr/>
    <div class="row">
      <div class="offset2 span8">
        {{> chatItems}}
      </div>
    </div>
  {{/if}}
</template>

El código anterior implementa la plantilla inicio, solo se generará contenido si hay un usuario autenticado, a eso lo determina {{#if currentUser}}. El contenido que se crea consta del área de texto con id="texto", el texto de ayuda y finalmente en una nueva fila (en la parte de abajo) se renderiza la plantilla {{> chatItems}}.

<template name="chatItems">
  <ul>
    {{#each entradas}}
      <li class="itemchat">
        <b>{{nombre}}: </b>{{{texto}}}
      </li>
    {{/each}}
  </ul>
</template>

Finalmente la plantilla chatItems en la cual se creará una lista con el contenido del chat. Esta plantilla contiene un bucle each que itera sobre la fuente de datos entradas de la cual hablaremos en breve. La fuente de datos contiene objetos que representan las entradas de chat, cada objeto posee la siguiente estructura:
{
  nombre: 'usuario',
  texto: 'texto de chat',
  timestamp: estampaDeTiempoDeLaEntrada
}
Puede observarse que se muestra el nombre en negrita <b>{{nombre}}: </b> y luego el texto está rodeado por tres llaves {{{texto}}} esto hace que si lo que se mostrará contiene código html, el browser lo interprete como tal.

Estilos personales:

Archivo: client/magmchat.css

textarea{ 
  resize:none; 
  width:100%;
  margin-bottom:10px; 
}

.vspace {
  margin-top:20px; 
}
Se definen las propiedades del área de texto, lo más importante es que ocupa el 100% del espacio horizontal. Luego se crea una clase que impone un espacio de 20 pixeles al objeto al que se le asigne, en nuestro caso se utiliza para crear un separador entre la barra de opciones y el área de texto.

/* para que los items vayan apareciendo en fade */
.itemchat{
  list-style:none;
  margin:0;
  padding:0;
  animation: fadein 1s;
  -moz-animation: fadein 1s; /* Firefox */
  -webkit-animation: fadein 1s; /* Safari and Chrome */
  -o-animation: fadein 1s; /* Opera */
}
@keyframes fadein {
  from {
    opacity:0;
  }
  to {
    opacity:1;
  }
}
@-moz-keyframes fadein { /* Firefox */
  from {
    opacity:0;
  }
  to {
    opacity:1;
  }
}
@-webkit-keyframes fadein { /* Safari and Chrome */
  from {
    opacity:0;
  }
  to {
    opacity:1;
  }
}
@-o-keyframes fadein { /* Opera */
  from {
    opacity:0;
  }
  to {
    opacity: 1;
  }
}​
Finalmente se crea una clase que permite a los items de chat aparecer de forma gradual o fadein, la cantidad de texto es para dar soporte a varios navegadores, las reglas   from { opacity:0; } to { opacity:1; } definen el grado de transparencia y de opacidad de inicio y fin, en la clase se define el tiempo, que en este caso es de 1 segundo.

Definición de la Colección (la base de datos):

Archivo: /chat.js

chat = new Meteor.Collection("chat");

Con este simple código que es compartido por cliente y servidor ya que se encuentra en la carpeta raíz /, creamos una colección que Meteor se encarga de persistir y que además informar los cambios a todos los clientes para que aquellas plantillas que la utilicen se actualicen automáticamente, esto es parte de la reactividad.

El código del cliente:

Archivo: client/magmchat.js

Accounts.ui.config({
  passwordSignupFields: 'USERNAME_ONLY'
}); 

El código anterior configura el paquete accounts para que el sistema de login requiera como datos de cuenta un usuario y su contraseña, el almacenamiento y administración es local.

Template.chatItems.entradas = function () {
  return chat.find({}, {sort: {timestamp: -1}, limit: 10});
};

El código anterior define la fuente de datos entradas que se itera en la plantilla chatItems. Solo destacaré que se buscan todas las entradas sin filtro alguno y que se retornan ordenadas por estampa de tiempo descendente, solo se retornan las últimas 10 entradas.

Template.inicio.events({
  "keyup #texto": function (evt, tpl) {
    if (evt.ctrlKey && evt.keyCode == 13) {
      $("#texto").val($("#texto").val()+"\n");
    } else if (evt.keyCode == 13) {
      var val=$("#texto").val();
      if (val.trim().length==0) {
        $("#texto").val("");
        return;
      }
      val = val.replace(/(\r\n|\n|\r)/gm,"<br/>");
      Meteor.call('enviarTexto',Meteor.user().username,val,
        function (error, result) { 
          if (error) {
            console.log(error);
          }
        }
      );
      $("#texto").val("");
    }
  }
});

Finalmente, la última porción de código, es la más extensa, pero no reviste complejidad. Se trata del único evento que se es observado, evento keyup de la caja de texto, si se presiona Ctrl+Enter, se agrega un retorno de carro al texto '\n' y nada más. Si solo se presiona Enter, se obtiene el texto ingresado, si se ha ingresado un texto válido, se reemplazan los caracteres de retorno de carro por la marca html que lo representa <br/>,  luego se llama a un método remoto llamado enviarTexto que recibe como parámetros el nombre del usuario logueado (Meteor.user().username) y el texto. A continuación analizamos la complementación del método remoto.

El código del server:

Archivo: server/metodos.js

Meteor.startup(function () {
  Meteor.methods({
    enviarTexto: function (usuario, txt) {
      chat.insert({
         nombre: usuario,
         texto: txt,
         timestamp: new Date()
      });
    }
  })
});

Como puede observarse, el método es de lo más sencillo, solo inserta en la colección un nuevo objeto con los datos que recibe como parámetros y genera un valor para la estampa de tiempo, este es el motivo principal por el cual se implementa el método en el server, para que la estampa de tiempo sea coherente y no dependa de los valores de tiempo de cada cliente.

Así es como ve:



Conclusión

Eso es todo, con poco código hemos creado un char totalmente funcional, el poder de Meteor para abstraernos de problemas de sincronización y comunicaciones en tiempo real es realmente impresionante.

Pueden descargarse el código fuente desde: https://github.com/magm3333/magmchat
Pueden probarlo en funcionamiento en: magmchat.meteor.com

Espero les sea de utilidad

Mariano

No hay comentarios.:

Etiquetas

pentaho (45) java (35) eclipse (23) jdbc (14) curso (13) tomcat (13) primefaces (12) db2 (11) mondrian (10) review (10) jsf (9) openI (9) pdi (9) prd (9) libro (8) plugin (8) musql (7) struts (7) javascript (6) spring (6) cdf (5) ctools (5) instalar (5) linux (5) mysql (5) data studio (4) hibernate (4) ireport (4) jasper (4) meteor (4) videocurso (4) eglu (3) eglubi (3) elearning (3) graphite (3) grupo eglu (3) jboos tools (3) mexico (3) openbits (3) packt (3) python (3) undec (3) websphere (3) applet (2) cde (2) dao (2) db2university (2) exelearning (2) flexigrid (2) hadoop (2) iua (2) kettle (2) moodle (2) node (2) olap (2) osbi (2) pivot4j (2) scorm (2) sql (2) stpivot (2) actionscript (1) amazon (1) autenticacion (1) avanzado (1) base de datos (1) big data (1) bigdata (1) bodoc (1) cambiar (1) ccc (1) cdc (1) chat (1) cloud (1) coffeescript (1) control de acceso (1) corti (1) csv (1) cuba (1) curso meteor undec (1) dashboard (1) datamart (1) dataptix.tv (1) datasource (1) datatable (1) db2 ExpressC (1) demonio (1) distancia (1) driver (1) driver jdbc (1) eglufiltertwolist (1) encapsulamiento (1) especialización (1) etl (1) excepciones (1) export (1) faces (1) federación (1) filas afectadas (1) filtertwolist (1) filtrado (1) flegrid (1) flex (1) google (1) google viz (1) hostname (1) html (1) i18n (1) ibm (1) identidad (1) indignación (1) instancias (1) inteligencia de negocios (1) jee (1) jpivot (1) l10n (1) la azada (1) la zaga de los confines (1) layout (1) liberado (1) libre (1) libro promoción (1) lob (1) marktplace (1) menu (1) meteor node javascript google oauth autenticacion (1) mobile (1) mongoDB (1) node.js (1) oauth (1) olap4j (1) open source (1) orm (1) persistencia (1) personalizada (1) prd5 (1) psw (1) publicidad (1) rad6 (1) recursividad (1) reporting (1) rock (1) saiku (1) script (1) servicio (1) sessiontimeout (1) sourceforge (1) spinneta (1) sqlserver (1) ssl (1) taller (1) troyanx (1) ubuntu (1) ucc (1) ui (1) web (1) web console (1) xampp (1) xml (1) xpath (1)

Seguidores