E aí pessoal, beleza?

No post anterior falamos sobre os controladores e boas práticas na sua utilização. Hoje vamos abordar um pouco sobre os services.

Services - O que são? Para que servem?

Service é o objeto usado para organizar e/ou compartilhar estados de objetos e as regras de negócio da aplicação. Ele é singleton, ou seja, há apenas uma instância disponível durante a vida útil da aplicação. Outra característica importante é a inicialização tardia (lazily instantiated), que só é efetuada quando o AngularJS identifica que tem algum componente dependente.

Opa, espera aí! O controller não é o lugar de controle da view? Logo, não é nele que eu tenho que ter as regras de negócio? Sim, o controller de fato controla a camada de visão, porém, não é ele que armazena as regras que são compartilhadas na aplicação. O controller gerencia apenas as regras referentes a view a qual está associado.  Vou enumerar porquê as regras devem ir para um service:

  1. O controlador é criado sempre que acessamos a view que o tem como dependência e é destruído assim que essa dependencia não é mais necessária, por exemplo, quando há mudança na rota e a view é substituída por uma nova. Então, quando queremos que o estado do objeto tenha o ciclo de vida independente da camada de visão, usamos service por ser singleton;
  2. Através da injeção de dependência do AngularJS, o service pode ser utilizado por toda a aplicação. O controlador tem a limitação de não ser instanciado pelo provider, o serviço $controller é o responsável por iniciá-lo. Aqui você tem mais detalhes sobre a injeção de dependência do AngulaJS, recomendo a leitura;
  3. O AngularJS dispõe de uma vasta opção de services, por exemplo o $http, para facilitar a comunicação remota. Além disso, é muito fácil criar o nosso próprio serviço, na sequência vou mostrar como fazer =);
  4. A centralização das regras em um service facilita na manutenabilidade e testabilidade do código.

Usando um service

Agora que expliquei alguns conceitos sobre services, vamos exemplificar a utilização do $window. Para utilizá-lo você deve adicionar a dependência no componente (controller, directive, filter ou service) que dependerá do serviço. No exemplo abaixo, que pode ser visto rodando aqui, vamos exibir um alerta de uma mensagem informada pelo usuário:

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
       
<!DOCTYPE html>
<html ng-app="app">
<head>
    <meta charset="utf-8" />
    <title>Blog do Gabriel Feitosa</title>
</head>
<body>
    <h1>Usando o service $window</h1>
    <!--Declaração do controlador AlertController e definição do nome que será usado para utilização 'ctrl'-->
    <div ng-controller="AlertController as ctrl">
        <fieldset>
            <legend>Criador de alertas</legend>
            <input ng-model="ctrl.alerta.mensagem" placeholder="Qual o mensagem de alerta?" />
            <br>
            <button ng-click="ctrl.enviar()">Enviar</button>
        </fieldset>
    </div>
    <footer>
        <hr/>
        <a href="http://www.gabrielfeitosa.com"> Blog do Gabriel Feitosa</a>
    </footer>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.3/angular.min.js"></script>
    <script>
        var app = angular.module('app', []);
        app.controller('AlertController', ['$window', function($window) {
            var self = this;
            self.alerta = {
                mensagem: ''
            };
            self.enviar = enviar;
            function enviar() {
                $window.alert('Mensagem criada : ' + self.alerta.mensagem);
            }
        }]);
    </script>
</body>
</html>

O controlador AlertController (linha 25) possui o $window como dependência. Quando o usuário informa a mensagem (linha 13) e a envia para o controlador (linha 15), utilizando a diretiva ng-click do botão, o controlador utiliza o serviço $window.alert (linha 34) para exibí-la em um alert na tela.

Na API de referência você pode verificar os services disponíveis no core do AngularJS.

Criando nosso próprio service

Agora que já sabemos como usar, vamos criar um serviço próprio. Você é livre para definir qual o nome do seu serviço, mas aconselho nunca utilizar o símbolo $ no começo do nome, pois os services do AngularJS começam com $, o que pode gerar conflito. Há três maneiras de se criar um serviço (factory, service e provider), recomendo que leia no stackoverflow a explicação dada por um usuário sobre as diferenças entre esses tipos.

No exemplo abaixo, foi criado um serviço MensagemFactory utilizando o factory(). Essa factory está sendo usada por dois controladores distintos (FofoqueiroControllerVizinhoChatoController). Ele pode ser visto 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
67
68
69
70
  

<!DOCTYPE html>
<html ng-app="app">
<head>
    <meta charset="utf-8" />
    <title>Blog do Gabriel Feitosa</title>
    <style type="text/css">
        button {
            background-color: red;
            color: white;
            font-weight: bold;
        }
    </style>
</head>
<body>
    <h1>Factory</h1>
    <div ng-controller="FofoqueiroController as fofoqueiro">
        <fieldset>
            <legend>Fofoqueiro Controller</legend>
            <input ng-model="mensagem" ng-change="fofoqueiro.fofocar(mensagem)" placeholder="Qual a fofoca de hoje?" />
            <br>
            <span>O fofoqueiro disse <b>{{fofoqueiro.falou()}}</b></span>
        </fieldset>
    </div>
    <br>
    <div ng-controller="VizinhoChatoController as vizinhoChato">
        <fieldset>
            <legend>Vizinho Chato Controller</legend>
            <span>O vizinho chato ouviu: <b>{{vizinhoChato.escutou()}}</b></span>
            <br>
            <button style="" ng-click="vizinhoChato.espalhaFofoca()">Espalhar Fofoca</button>
        </fieldset>
    </div>
    <footer>
        <hr/>
        <a href="http://www.gabrielfeitosa.com"> Blog do Gabriel Feitosa</a>
    </footer>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.3/angular.min.js"></script>
    <script>
        var app = angular.module('app', []);
        app.controller('FofoqueiroController', ['MensagemFactory', function(Mensagem) {
            var self = this;
            self.falou = Mensagem.get;
            self.fofocar = Mensagem.set;
        }]);
        app.controller('VizinhoChatoController', ['MensagemFactory', function(Mensagem) {
            var self = this;
            self.escutou = Mensagem.get;
            self.espalhaFofoca = Mensagem.alertar;
        }]);
        app.factory('MensagemFactory', function($window) {
            var mensagem = {
                texto: ''
            };
            return {
                get: function() {
                    return mensagem.texto;
                },
                set: function(tx) {
                    mensagem.texto = tx;
                },
                alertar: function() {
                    $window.alert(mensagem.texto);
                }
            }
        });
    </script>
</body>
</html>

O controlador FofoqueiroController possui as functions falou() (linha 35) e fofocar() (linha 36). Quando um usuário fofoca alguma coisa utilizando o input e envia essa fofoca, o método fofocar() irá chamar a função Mensagem.set, passando como parâmetro a mensagem (linha 14).

O controlador VizinhoChatoController possui a function escutou() (linha 41) , que nada mais é que a função Mensagem.get. Quando a mensagem é modificada pelo FofoqueiroController, ele está ,na verdade, modificando o estado da mensagem do service criado. Por esse motivo, o estado da mensagem exibida no VizinhoChatoController é automaticamente alterado também.

Como podemos ver no exemplo do Fofoqueiro, os services também tem suas dependências. Assim como declaramos a dependência no controlador, a MensagemFactory tem igualmente uma dependência do $window.

Como boa prática, utilizo o factory para minhas regras de negócio e o service para realizar a comunicação externa (comunicação com o servidor). No caso do provider eu o utilizo quando preciso configurar algum service dentro do config(), por exemplo, quando quero setar variáveis de ambiente em um service.

Por hoje é isso pessoal, espero que tenham curtido o post.

Abraços e até a próxima!