Front End

11 ago, 2015

Como trabalhar com AngularJS – o guia absolutamente completo

Publicidade

Com o grande crescimento recente da Concrete Solutions, estamos trabalhando em uma série de posts para explicitar a nossa forma de trabalhar com as tecnologias que mais usamos. A ideia é orientar tanto os novos “concreteiros” quanto leitores que por ventura se interessem pelo nosso formato. Recentemente, o Thiago Lioy fez esse post com nossas práticas de iOS. Hoje, é a vez de falarmos sobre AngularJS.

Motivação

Existem diversos frameworks de JavaScript atualmente. Então por que você deveria usar AngularJS em vez dos outros?

Modularização

A modularização facilita o desenvolvimento, a configuração e principalmente os testes da sua aplicação. O AngularJS vem com um mecanismo de Injeção de Dependências built-in, por exemplo, que transforma a tarefa de dividir a sua aplicação em pequenos módulos em algo trivial.

Two-way Data Binding

Uma das features mais polêmicas do AngularJS é o Two-way Data Binding. Apesar de ser muito útil e um dos grandes fatores para o sucesso do framework no início, ela pode apresentar problemas de performance caso não seja usada corretamente.

Extensibilidade

Poder estender o HTML com o AngularJS, usando diretivas, é a feature mais incrível do framework. Diretivas são poderosas ferramentas para modificar e manipular o DOM, além de ter a facilidade de reuso em toda a aplicação.

Ferramentas

Sublime Text

1

O editor mais usado para desenvolver aplicações com AngularJS continua sendo o Sublime Text. Além de ser bem leve e fácil de usar, ele possui um package (plugin) específico para o AngularJS.

Jasmine

2

O Jasmine foi desenvolvido para ser usado em BDD (behavior-driven development), porém geralmente é usado em AngularJS para testes unitários e TDD (test-driven development).

Karma

3

Karma é um test runner feito para o AngularJS (apesar de atualmente você poder usá-lo com outros frameworks JavaScript). O principal objetivo do Karma é automatizar os testes em diversos browsers com um simples comando. Uma das vantagens é que ele suporta diversos tipos de testes, como unitários, de integração e E2E.

Bower

4

O Bower, criado como um projeto open source do Twitter, foi desenvolvido para facilitar o uso de bibliotecas e pacotes em um projeto web. É possível inclusive, desenvolver pacotes próprios e distribuí-los em diferentes projetos de forma fácil.

Grunt

5

Em diversas ocasiões, nós desenvolvedores enfrentamos tarefas repetitivas que podem ser facilmente automatizadas. É aí que entra o Grunt. Ele é usado para: minificação, linting de JavaScript e geração de builds.

Exemplo de aplicação

Setup

Para começarmos o nosso setup, precisamos instalar o Bower. Assumindo que você já tem o NPM instalado, precisamos apenas de:

$ npm install -g bower

Agora, crie uma pasta chamada app-exemplo, entre nela e crie um arquivo de configuração do Bower:

$ mkdir app-exemplo
$ cd app-exemplo
$ bower init

Aceite todos os defaults e o seu bower.json deverá estar próximo disto:

/// bower.json
{
  "name": "app-exemplo",
  "version": "0.0.0",
  "authors": [
    "Matheus Lima <matheusml90@gmail.com>"
  ],
  "license": "MIT",
  "ignore": [
    "**/.*",
    "node_modules",
    "bower_components",
    "test",
    "tests"
  ]
}

Agora, para instalar o AngularJS, devemos adicionar a dependência no Bower. E para isso basta digitar:

$ bower install --save angular

Observe que foi criada uma pasta chamada bower_components. Nessa pasta estarão todas as dependências do Bower. Além disso, o arquivo bower.json foi automaticamente modificado, ficando desta forma:

/// bower.json
{
  "name": "app-exemplo",
  "version": "0.0.0",
  "authors": [
    "Matheus Lima <matheusml90@gmail.com>"
  ],
  "license": "MIT",
  "ignore": [
    "**/.*",
    "node_modules",
    "bower_components",
    "test",
    "tests"
  ],
  "dependencies": {
    "angular": "~1.4.3"
  }
}

É altamente recomendável adicionar a pasta bower_components no .gitignore. Para fazermos isso, basta primeiramente criar o arquivo:

$ touch .gitignore

E dentro do .gitignore, precisamos deixá-lo desta forma:

/// .gitignore
bower_components

Agora, falta criar o index.html da nossa aplicação:

$ mkdir app
$ cd app
$ touch index.html

E, nele, importar o arquivo minificado do AngularJS que veio do Bower, para que a aplicação possa funcionar.

/// index.html
<html>
  <head>
    <title>AngularJS</title>
  </head>
  <body>
    <h1>AngularJS</h1>
    <script type="application/javascript" src="../bower_components/angular/angular.min.js"></script>
  </body>
</html>

Precisamos também de um servidor para rodar nossa aplicação e ver que nosso index.html funciona. Vamos usar o http-server, que pode ser baixado pelo NPM:

$ npm install -g http-server
$ cd ..
$ http-server

Agora, basta entrar em http://localhost:8080/ e devemos visualizar algo próximo disto:

6

Para padronizar e facilitar o start e os testes da aplicação, precisamos configurar um package.json (que é utilizado pelo NPM):

$ npm init

Aceite todos os defaults e ele deverá ficar semelhante a isto:

//package.json
{
  “name”: “app-exemplo”,
  “version”: “1.0.0”,
  “description”: “”,
  “main”: “index.js”,
  “scripts”: {
      “test”: “echo \”Error: no test specified\” && exit 1"
   },
  “author”: “”,
  “license”: “ISC”
}

Para que possamos iniciar a aplicação com apenas um comando, vamos editar o arquivo adicionando apenas uma linha:

///package.json
{
  “name”: “app-exemplo”,
  “version”: “1.0.0”,
  “description”: “”,
  “main”: “index.js”,
  “scripts”: {
      "start": "http-server ./app -a localhost -p 8080",
      “test”: “echo \”Error: no test specified\” && exit 1"
   },
  “author”: “”,
  “license”: “ISC”
}

Com essa modificação, podemos rodar a aplicação da seguinte forma:

$ npm start

E acessando http://localhost:8080/ você deverá ver isto:

7

Arquitetura

Agora que fizemos o setup básico, podemos partir para o modelo de arquitetura de aplicações em AngularJS que usamos aqui na Concrete Solutions.

1. Estrutura
Todos os nossos Controllers, Services, Factories e Diretivas seguem a mesma estrutura:

(function() {
    'use strict';
    angular
        .module('app')
        .controller('AuthController', AuthController);
    
    function AuthController() {
        /* código */
    }
})();

Se você não entendeu a primeira e a última linhas, esse padrão bem comum em projetos com JavaScript é chamado de IIFE. Recomendo este texto para você entender todos os detalhes dele.

Outro padrão bem comum que nós usamos é o ‘use strict’, que serve resumidamente para melhorar o processo de verificação de erros no código.

E, por último, vale notar que nós usamos uma abordagem Top-Down, em que as declarações ficam na parte de cima do arquivo, e a implementação fica em baixo.

2. Injeção de Dependências
Nós usamos o método $inject para fazer DI:

(function() {
    'use strict';
    angular
        .module('app')
        .controller('AuthController', AuthController);
    AuthController.$inject = ['$location', 'AuthService'];
    
    function AuthController($location, AuthService) {
        /* código */
    }
})();

A grande vantagem é a facilidade na minificação de arquivos (visto que as dependências são trazidas como Strings).

3. Controllers

function AuthController($location, AuthService) {
    var vm = this;
    vm.login = login;
    function login(email, password) {
        AuthService.login(email, password).then(function() {
            $location.path('/');
        });
    }
}

Tentamos manter os Controllers o mais lean possível, com pouca lógica, e seguindo o Single Responsability Principle. Dessa forma, eles ficam mais fáceis de manter, ler e testar.

Usamos também o padrão do ControllerAs com a variável vm, proposta pelo John Papa. Uma das vantagens é a não necessidade de se importar o $scope nos Controllers.

4. Comunicação com o Back-End
Preferimos encapsular chamadas $http em Services:

function AuthService($http) {
    this.login = function(email, password) {
        var request = {
            username: email,
            password: password
        };
        return $http.post(url, request);
    };
}

Um dos focos dessa abordagem é o Separation of Concerns. Ou seja, não é responsabilidade dos Controllers lidar com a lógica da comunicação com o Back-End, mas sim de um serviço feito apenas para lidar com isso.

5. Factory
Uma das formas de se compartilhar dados em uma aplicação com AngularJS é o uso das Factories. Como elas são singletons, os dados não se perdem na mudança de contexto. Um bom caso de uso é a criação de uma Session:

function Session($cookieStore) {
    return {
        create: create,
        destroy: destroy,
        isAuthenticated: isAuthenticated
    };
    
    function create(id) {
        $cookieStore.put('session', id);
    }
    function destroy() {
        $cookieStore.remove('session');
    }
    function isAuthenticated() {
        !!$cookieStore.get('session');
    }
}

Nesse exemplo, podemos acessar de qualquer Controller a sessão do usuário e verificar se ele está autenticado ou não.

Uma dúvida recorrente é a diferença entre Factories e Services, que eu expliquei anteriormente neste artigo.

6. Diretivas
Assim como Controllers, as Diretivas devem ser desenvolvidas com o Single Responsability Principle em mente. Se uma Diretiva resolve múltiplos problemas ao mesmo tempo, ela dificilmente será reaproveitada e perderá todo o sentido de existir (que é justamente o reuso).
O exemplo abaixo mostra uma Diretiva que dá foco a um input:

function NgFocus($timeout) {
    return {
        restrict: 'A',
        link: function(scope, element, attrs) {
            $timeout(function() {
                element[0].focus();
            }, 0);
        }
    };
}

Note que a Diretiva em questão tem apenas um objetivo. Se ela, além de dar foco ao input, tivesse que adicionar a cor verde no background, certamente perderia o reuso na maioria dos casos.

Um bom texto sobre Diretivas é este aqui, do Todd Motto.

Continuous Delivery

É comum investirmos alguns sprints no início dos projetos para arquitetar a infraestrutura que dará suporte para os desenvolvedores da primeira linha de código até a última. Isso significa montar e estruturar a integração contínua, o deployment contínuo e automatizar os processos de desenvolvimento e suporte. As principais vantagens do Continuous Delivery são:

  • Aumento da transparência;
  • Aumento do feedback;
  • Releases frequentes;
  • Maior confiança em cada entrega;
  • Desenvolvedores podem focar mais na qualidade do código e menos em builds e deploys.

Para automatizar nosso processo de CI, escolhemos o Jenkins como ferramenta. Primeiro devemos decidir o que o Jenkins deve fazer no processo de deployment, que pode ser resumido em 6 comandos:

$ npm install grunt -g
$ npm install bower -g
$ npm install
$ bower install
$ npm test
$ grunt dev
  1. Instalar o Grunt
  2. Instalar o Bower
  3. Instalar todos os packages do NPM
  4. Instalar todos os packages do Bower
  5. Rodar os testes usando o Karma
  6. Gerar o Build usando o Grunt

Após esse procedimento, o Jenkins gera um relatório de cobertura de testes (que pode ser configurado pelo Karma desta forma):

8

Esse relatório tem grande utilidade para verificar a qualidade do código durante todo o andamento do projeto.

Conclusão

Para quem ainda tiver dúvidas, uma boa dica é a minha série sobre AngularJS no YouTube, que vai dos primeiros passos até a construção de diretivas avançadas:

Se você tem alguma crítica ou sugestão me contate no Twitter ou deixe um comentário aqui embaixo. E se gostou do texto, compartilhe com quem possa ter interesse!