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!

Anúncios
Etiquetado , , , , , , , , ,

3 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!!!

  3. crazzycraft5 disse:

    Cara, vc é incrível! Explica de uma forma que todo leigo(como eu) gostaria de aprender. Estou aguardando outros posts sobre Node.Js Parabéns!

Deixe um comentário

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: