WebSocket com Node.js – Parte 2: Temporizador

Este post se refere a segunda parte da série WebSocket com Node.js, clique aqui e confira a primeira parte.

Também vamos utilizar o AngularJS para o front-end, afinal há muitas dúvidas de como integrá-lo com o Node.js – mais especificamente falando com o socket.io.

A ideia aqui é que um servidor registre a data e hora e guarde numa lista a cada um segundo. Cada vez que isto acontecer, o servidor irá emitir a lista com todos os horários registrados. Deste modo, todos os clientes que estiver aguardando esta lista irão recebê-la a cada segundo. O maior desafio, que nem é tão grande assim, é que cada cliente que entre depois de algum tempo e que também queira receber esta lista, consiga receber não só as notificações de tempo a partir do momento que passou a se comunicar com o servidor, mas de todo o histórico passado.

Inicialmente, vamos ao setup! Lembrando que você precisa ter o Node.js instalado em sua máquina (se não tiver, é só baixar e instalar – não tem segredo).

Crie um diretório com qualquer nome (eu chamei de simple-clock) e lá dentro crie um arquivo chamado package.json

package.json

Neste arquivo, adicione os módulos que iremos trabalhar com as seguintes linhas:

{

  name: “application-name”,

  version: “0.0.1”,

  dependencies: {

    express: “3.4.7”,

    “jade”: “*”,

    “socket.io”: “*”

  }

}

O express é a framework para criarmos rotas e o nosso servidor – é o módulo mais famoso do Node.js, base para praticamente toda a aplicação web.

O jade é a template-engine que utilizaremos para desenhar a nossa tela para o client. O socket.io dispensa apresentações.

Agora vá no Command Prompt, navegue até o diretório que você criou (no mesmo local onde encontra-se o arquivo) e digite:
npm install

O gerenciador de módulos do Node.js irá fazer o download de todos os módulos que declaramos no arquivo package.json, dentro de um diretório chamado node_modules.

Ao término, crie também mais três diretórios na mesma estrutura: public, routes e views. Dentro do diretório public, crie o diretório javascripts. Entre neste diretório e crie um arquivo chamado app.js (depois eu explico).

Entre no diretório routes e crie o arquivo index.js.

Entre no diretório views e crie os arquivos layout.jade e index.jade.

Na raiz do diretório (no local onde encontra-se o arquivo package.json) e crie o diretório app.js.

Tudo deverá ficar assim:

diretorio-raiz
                node_modules
                               express
                               – jade
                               – socket.io
                public
                               javascripts
                                              
app.js
                routes
                               – index.js
                views
                               index.jade
                               layout.jade
                – app.js
                package.json

Vamos começar a editar o arquivo app.js!

app.js

Este arquivo é o principal do Node.js! Ele adiciona as dependências, configura as rotas e o servidor, como domínios e portas que serão utilizadas.

O código completo deste arquivo ficará assim:

var express = require('express');
var routes = require('./routes');
var http = require('http');
var path = require('path');
var SocketIO = require('socket.io');

var app = express();
var server = http.createServer(app);
var io = SocketIO.listen(server);
var timeHistory = [];

setInterval(function(){
    timeHistory.unshift(new Date().toJSON());
    io.sockets.emit('time', {time: timeHistory});
}, 1000);

app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');
app.use(app.router);
app.use(express.static(path.join(__dirname, 'public')));

app.get('/', routes.index);

server.listen(3333);

Nas cinco primeiras linhas, temos a importação dos módulos que o nosso servidor Node.js utilizará. Você já foi apresentando ao express e ao socket.io. O routes é apenas um atalho para o diretório que criamos anteriormente (que hospeda o arquivo index.js). O http e o path são módulos built-ins do Node.js: o primeiro abstrai um monte de complexidade que envolve o protocolo http e disponibiliza métodos para deixar a nossa vida mais fácil. O segundo é um facilitador para trabalhar com caminhos físicos, uma vez que o pessoal que programa para a web sempre se atrapalha um pouquinho nisto, com a questão dos recursos relativos e absolutos.

Depois temos o seguinte

var app = express();
var server = http.createServer(app);
var io = SocketIO.listen(server);
var timeHistory = [];

A primeira linha é uma variável para o express já inicializado. A segunda linha cria o servidor a partir do express e retorna este servidor na variável server. A terceira linha cria uma instância do Socket vinculado a este servidor que criamos na linha anterior. Por fim, temos uma variável que irá armazenar cada tick de nosso temporizador, registrando todos os horários. É esta variável que será emitida para todos os nossos clientes que estarão aguardando esta mensagem.

Na sequência, temos TODO o funcionamento do socket no servidor! Simplesmente configuramos um cara para ficar executando a função da cada 1000 milissegundos (1 segundo). Esta função apenas pega a data e hora atual e armazena no topo da lista (a variável timeHistory). Depois disto, ela pega esta lista e emite (“emit”) para todos os clientes que estiverem plugados (“on”) no canal “time” (primeiro argumento).

setInterval(function(){
    timeHistory.unshift(new Date().toJSON());
    io.sockets.emit('time', {time: timeHistory});
}, 1000);

Agora vamos a última parte do servidor:

app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');
app.use(app.router);
app.use(express.static(path.join(__dirname, 'public')));

app.get('/', routes.index);

server.listen(3333);

Estas configurações se referem ao client. Estamos dizemos onde encontra-se as nossas views (os arquivos com a extensão jade) e qual é a view engine que irá interpretar estes arquivos.

Também iremos utilizar o router e definimos qual é o diretório que ficará público.

Por fim, temos a rota e a porta! Todos aqueles que caírem na raiz serão redirecionados para o index, na porta que estaremos escutando. Neste caso, se estiver rodando de sua máquina, por padrão o endereço será http://localhost:3333.

Vamos agora editar o arquivo index.js, que está dentro da pasta routes.

routes/index.js

exports.index = function(req, res){
  res.render('index', { title: 'Express' });
};

É bem simples: exportamos a função index, que recebe dois argumentos (request e response), responsável por renderizar a viewindex”, que será criada utilizando o jade no próximo passo. Como argumento, passamos o parâmetro “title”, configurado como “Express”, mas você pode colocar o nome que bem entender só para ver como funciona. Você verá a utilização deste parâmetro mais abaixo.

Do lado do servidor, o socket está pronto. Vamos começar com o client agora.

views/layout.jade

Edite o arquivo layout.jade que está na pasta views.

doctype 5
html(ng-app="app")
  head
    title= title
  body
    block content
    script(src='/socket.io/socket.io.js')
    script(src='http://ajax.googleapis.com/ajax/libs/angularjs/1.2.5/angular.min.js')
    script(src='/javascripts/app.js'

Aqui acho que nem vale perder tempo explicando. Simplesmente é um template básico que servirá de sandbox para todos aqueles que quiserem utilizá-lo. Como analogia, é praticamente a mesma coisa da master page / layout template de diversas linguagem.

Talvez você somente estranhe o fato de que estamos desenhando o nosso HTML sem as marcações padrões. Isto, basicamente, é o que o Jade faz: interpreta uma sintaxe e uma hierarquia e simplesmente monta o HTML para você.

Observe que temos também a importação dos scripts socket.io.js, do AngularJS (que você já ouviu falar bastante aqui no blog) e do app.js, que ainda não criamos.

O AngularJS seria opcional ao nosso propósito, mas acho que muitos tem dúvidas (ao menos, eu tinha) de como trabalhar com WebSocket, diretivas e bindings com o AngularJS.

Todavia, nesta série sobre WebSocket teremos outros exemplos sem utilizar o AngularJS, para quem não tem interesse em aprender a framework.

Agora vamos editar o index.jade

views/index.jade

extends layout

block content
  h1= title
  p Welcome to #{title}

  div#container(ng-controller="SocketController")
    ul#messages
      li(ng-repeat="item in messages")
          {{item}}

 

A primeira palavra, extends, recebe o layout que criamos no passo anterior. Isto indica que podemos ter quantos layouts quisermos. Uma vez que utilizamos e declaramos um layout, o jade injeta todo o conteúdo descrito por ele nesta nossa view.

Na sequência temos o block. O block é uma função de associação para o jade saber onde ele deverá inserir o conteúdo da view dentro do layout que criamos previamente.

Outro ponto a destacar é o #{title}. Lembra do argumento “title” que criamos no arquivo index.js na routes? É assim que ele passa a informação do js para o html.

Por fim, passamos o controller SocketController para a diretiva do AngularJS. Este controller está dentro do arquivo “public/javascripts/app.js”, que iremos criar na sequência. Aqui estamos informando para o compilador do AngularJS que tudo o que está dentro desta div poderá fazer o binding de informações do Controller.

No caso, iremos fazer um looping na model “messages” do controller e adicionar uma li para cada mensagem encontrada.

Para finalizar, falta só o arquivo app.js, que está no public/javascripts.

public/javascripts/app.js

angular.module('app', []).
    factory('socket', function($rootScope){
        var socket = io.connect();

        return {
            on: function(eventName, callback){
                socket.on(eventName, function(){
                    var args = arguments;
                    $rootScope.$apply(function(){
                        callback.apply(socket, args);
                    });
                });
            },

            emit: function(eventName, data, callback){
                socket.emit(eventName, data, function(){
                    var args = arguments;
                    $rootScope.$apply(function(){
                        if(callback){
                            callback.apply(socket, args);
                        }
                    });
                });
            }

        };
    });

function SocketController($scope, socket){
    $scope.messages = [];

    socket.on('time', function(data){
        $scope.messages = data.time;
    });
}

O ponto mais chocante em relação ao que você viu de AngularJS neste blog é a necessidade de criar uma factory do Socket.IO para podermos utilizarmos em nosso Controller, que é quando a model conversa com o socket.

A analogia é criarmos uma “classe” (factory) com dois métodos: “on” e “emit” para utilizarmos em nosso controller. Nesta “classe”, estamos “instanciando” o script chamado socket.io.js e utilizando-o para implementar estes dois métodos.

Observe também que estamos criando uma variável chamada socket, que é criada a partir da conexão com o servidor (io.connect()). Se tivéssemos criando um client para escutar um outro servidor, que não fosse ele mesmo, poderíamos passar a url no método connect.

Depois temos o controller de fato, recebendo a model como parâmetro e esta “classe” que acabamos de criar.

Ali está bem simples: todas as vezes que o servidor emitir uma mensagem no canal “time”, populamos a model “messages”, que já está em binding lá no index.jade.

Ou seja, o client está escutando o tempo inteiro o canal “time”… Se o servidor emitir esta mensagem, ele toma a ação de sobrescrever a model messages.

É isto! Agora é só abrir o prompt, entrar na raiz do projeto e digitar:
node app.js

Isto irá inicializar o nosso servidor.

Abra o browser e digite http://localhost:3333.

Se tudo deu certo, você passará a visualizar a data e hora, segundo a segundo. Experimente abrir diversas abas do browser e alterar o tempo de atualização no socket do lado do servidor, enfim, fuce bastante!

Caso tenha dado errado, clone o meu repositório no github e faça a comparação:
https://github.com/evenancio/simple-clock-websocket-angularjs

Na próxima parte, vamos fazer um painel de atendimento, semelhante àqueles que encontramos em hospitais.

Até a próxima!

Etiquetado , , , , , , , , ,

2 pensamentos sobre “WebSocket com Node.js – Parte 2: Temporizador

  1. Aguardando proxima aula, estou implementando algo do tipo que você comenta no final..
    “painel hospital”, mas vai ser um painel que recebe dados de sensores nos quartos e mostra na tela a quantidade de soro que o paciente tem, caso acabe o soro, vou enviar alertas as enfermeiras.

    Usando alguns hardwares e um servidor no Raspberry PI.

  2. fabiohbarbosa disse:

    Parabéns, ótima didática e ótimo conteúdo!!
    Abraços!!!

Deixe uma resposta

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s

%d blogueiros gostam disto: