Fala galera, beleza?

Depois de um mês atípico (muito trabalho e organizando casamento) e um começo de mês com ombro deslocado, estou de volta para delírio de vocês heheheh (menos né?!).

Hoje vou falar sobre aplicações em tempo real utilizando o padrão ajax de atualização periódica (periodic refresh) e como o AngularJS pode nos ajudar a implementá-lo. Deixa eu te fazer uma pergunta, você gostaria de ficar apertando F5 para verificar se há uma nova notificação no Facebook? Ou se chegou aquele e-mail que você tanto esperava no Gmail? Eu acho que não, né?

A fim de tornar a experiência do usuário a mais agradável possível, muitas aplicações utilizam dados em tempo real (ou quase) e até criam uma certa dependência dessa tecnologia, pois sem ela jamais teriam se popularizado.

Vamos ver esse refresh em funcionamento? Você tem Twitter? Se sim, acesse ele e depois abra o console do browser (aperte F12, abestado!). Na aba network, você pode verificar que periodicamente o twitter faz um GET, para checar se há alguma novidade a ser exibida ao usuário. Conforme mostra a figura abaixo:

Twiter Console

Para exemplificar, vamos criar uma simples sala de bate-papo. Para o backend, utilizei a plataforma Java e para o frontend, a implementação foi feita com AngularJS usando o service $timeout.

$timeout

A galera do AngularJS resolveu encapsular o window.setTimeout dentro desse service. Quando o $timeout é chamado, ele retorna uma promise que será resolvida quando o delay for concluído e a função de timeout, caso exista, for executada.

Esse service tem a seguinte estrutura:

$timeout([fn], [delay], [invokeApply], [Pass]);

  • fn: A função a ser executada quando o delay acabar;
  • delay: O tempo limite em milissegundos. Valor default é zero.
  • invokeApply: Se o parâmetro for marcado como false o modelo de _durty checking _será ignorado, caso contrário, a função de tempo limite (fn) será chamada dentro do bloco $apply.
  • Pass: Aqui você pode passar parâmetros para a função a ser executada (fn).

O $timeout ainda nos fornece uma maneira de cancelar as tarefas que estão agendadas. Para isso, há o método $timeout.cancel([promise]). Ele recebe como parâmetro uma promise _e, como resultado da execução, essa _promise será resolvida com uma rejeição (rejection).

Backend com Java

Para o backend, usei os frameworks RestEasy para expor a API e o provider Jackson para consumir e produzir os objetos em JSON. Além disso, o projeto está com maven, utiliza Java 8 e está sendo executado no Tomcat. Ahhh! Ia esquecendo, está hospedado no heroku.

A aplicação implementada é bem simples, mas como não é o foco desse post, fiz só o necessário para o frontend funcionar. Os fontes do projeto estão no github.

A API exposta para uso consiste em:

  • GET /mensagens: listar as mensagens do chat;
  • POST /mensagens: enviar uma nova mensagem para a sala de bate papo;
  • DELETE /mensagens: apagar todas as mensagens da sala;

Frontend com AngularJS

Agora vamos falar da parte que mais nos interessa. Mas antes, como não sou lá essas coisas na parte de design, peguei a base do template para o chat nesse link aqui óh.

O gist abaixo apresenta a implementação da lógica necessária para realizar a atualização dos dados do nosso chat. O projeto completo pode ser visto no github e rodando aqui.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
(function() {
   angular.module("app")
    .factory("ChatFactory", function($http, $timeout) {
      var promise;
      var URL = "http://gf-chat.herokuapp.com/rest/mensagens/";
      var mensagens = [];
      var aberto = false;
      var contador = 5;

      return {
        entrar: entrar,
        listar: listar,
        cadastrar: cadastrar,
        isAberto: isAberto,
        sair: sair,
        getContador: getContador
      };

      function entrar() {
        aberto = true;
        ativarRefresh()
      }

      function ativarRefresh() {
        contador--;
        if (contador === 0) {
          atualizar();
          contador = 5;
        }
        promise = $timeout(ativarRefresh, 1000);
      }

      function sair() {
        $timeout.cancel(promise);
        aberto = false;
      }

      function atualizar() {
        $http.get(URL)
          .success(function(data) {
            mensagens = data;
          });
      }

      function cadastrar(usuario, texto) {
        var msg = {
          usuario: usuario,
          texto: texto
        }
        $http.post(URL, msg);
      }

      function getContador() {
        return contador;
      }

      function isAberto() {
        return aberto;
      }

      function listar() {
        return mensagens;
      }
    });

})();

Analisando o gist acima, o acesso ao chat é feito pelo método entrar (linha 19). Ele é o responsável por ativar o refresh periódico através do método ativarRefresh (linha 24). Perceba que dentro desse método implementei um contador. Esse contador é decrementado a cada um segundo (1000 milissegundos) e quando o seu valor for zero o método atualizar (linha 27) será chamado. O contador está sendo utilizado na tela para informar ao usuário quanto tempo falta para a sala ser atualizada.

Os outros métodos apresentados no gist são:

  • cadastrar (linha 45):  responsável por enviar ao servidor uma nova mensagem;
  • sair (linha 33): é aqui que o método $timeout.cancel é chamado para finalizar a atualização periódica;
  • getContador (linha 53): retornar o contador a fim de informar quanto tempo falta para a próxima atualização do chat;
  • isAberto (linha 57): informar se o chat está aberto ou não.
  • listar (linha 61): listar as mensagens da sala de bate-papo.

Então é isso pessoal, essa é uma das formas de termos uma aplicação em tempo real. Fico esperando suas dúvidas e sugestões.

Só mais duas coisinhas:

  1. Para fazer a barra de rolagem automática do chat usei essa diretiva aqui;
  2. Esse artigo foi baseado em um post do Java Code Geeks.

Abraços e até a próxima!