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. 

Imagen eliminada.

 

 

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="https://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:

Imagen eliminada.

 

 

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

 

Hola, mira que no se como llegue aquí jeje pero lo bueno es que llegue. Si he visto las tutos en ingles de otros chats con meteor, pero me he estado rompiendo la cabeza con el paquete streams desde mrt, ya que esta rota, hay q arreglarlo, etc.Veo que tu no usas tal paquete así que probare tu código de manera que sigo mi curva de aprendizaje. Mucho gusto y sigue así.

Hola, gracias por tu comentario.

Respecto a streams, en la última versión de meteor, funciona muy bien.

Este es un simple chat con estado, pero si quieres algo sin estado, streams es la respuesta más adecuada.

Saludos

Mariano

 

 

 

 

Excelente tutorial, gracias esta bien explicado y funciona a la perfeccion lo unico que hice diferente al inicio es meteor create magmchat por mrt create magmchat al respecto tengo dudas mrt es una utilidad Los datos se del chat se mantiene como lo gestiona o guarda meteor Como puedo poner fecha es decir usuario, fecha y texto enviado. Gracias Mariano esta genial el tuto.

 Hola,

mrt es un wrapper de meteor, aumenta la funcionalidad y te permite utilizar los smart packages, o sea los paquetes publicados en atmosphere. Para agregar la fecha solo debes modificar el template, por ejemplo:

({{timestamp}}) <b>{{nombre}}: </b>{{{texto}}}

 

Saludos

Mariano

 

Gracias por el tutorial, me sirvió de mucho. Tengo una consulta de la carga de las plantillas: al iniciar la aplicación y como todavía no me eh logeado::

1- la plantilla inicio ya se cargo o descargo al cliente?
2- como puedo tener varias vistas en la misma aplicación, digamos una vista con un login personalizado y otra vista con el chat.

Gracias de antemano.

En respuesta a por miguelfausto (no verificado)

Estimado, me alegro que te haya servido.
Las preguntas que haces son no triviales para contestarlas en un par de líneas, te recomiendo hacer lecturas más profundas sobre el tema.
-Comienza por: http://docs.meteor.com/
-Tienes un libro excelente: https://www.discovermeteor.com/ que actualmente tiene disponible la decarga del capítulo 3 que trata el tema Templates.
Como si lo anterior fuera poco aquí tienes un post sobre recursos de Meteor: http://yauh.de/articles/376/best-learning-resources-for-meteorjs

Saludos
Mariano