Como trabalhar com AngularJS — O Guia Absolutamente Completo

Olá, meu nome é Matheus Lima e eu trabalho como Desenvolvedor Web na Concrete Solutions. Como nos últimos anos temos visto um crescimento grande na empresa, o que gera diversas vagas, resolvemos criar uma série de posts relatando a nossa forma de trabalhar com as tecnologias que mais usamos, tanto para desenvolvedores novatos na Concrete, quanto para leitores que por ventura se interessem na nossa forma de trabalhar com AngularJS.


Motivação

Existem diversos frameworks de JavaScript atualmente. Então porque você deveria usar AngularJS ao invés 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 começo, ela pode apresentar problemas de perfomance caso não seja usada corretamente.

Extensibilidade

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

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

Jasmine

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

Karma

Karma

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, integração e E2E.

Bower

Bower

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

Grunt

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 a 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 disso:

/// 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 dessa 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 dessa 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 disso:

localhost:8080

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 isso:

///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 isso:

index.html

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 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 esse post 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 e sim de um serviço feito apenas para lidar com isso.

5. Factory
Uma das formas de se compartilhar dados numa aplicação com AngularJS é feita com 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 mostrado, 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 nesse post aqui.

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 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);
}
};
}

Nota-se 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 do mesmo, ela certamente perderia o reuso na maioria dos casos.
Um bom post sobre Diretivas é esse aqui do Todd Motto.


Continuous Delivery

É comum investirmos alguns sprints no início dos projetos para arquitetar a infraestrutura que dará suporte aos 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 deve-se decidir o que o Jenkins deve fazer no processo de deployment, que podem ser resumidos 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 dessa forma):

Cobertura de testes gerado pelo Jenkins

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


Conclusão

Para quem ainda possuir mais 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ê gostou do post não se esqueça de dar um ❤ aqui embaixo!
E se quiser receber de antemão mais posts como esse,
assine nossa newsletter.

JSCasts

É difícil encontrar conteúdo bom e atualizado em português. Com isso em mente criamos o JSCasts, onde você vai se manter em dia com o JavaScript e todo o seu ecossistema de forma fácil e interativa.

Cursos:

Obrigado por ler! ❤