#Introdução
O objetivo deste guia é apresentar um conjunto de boas práticas e diretrizes para uma aplicação AngularJS. Estas boas práticas são baseadas em:
- Código fonte do AngularJS
- Códigos fonte ou artigos que li
- Minha própria experiência
Nota: Este guia ainda é um rascunho, seu objetivo principal é ser construído pela comunidade, então ao contribuir você será muito apreciado por toda ela.
Neste guia você não vai encontrar diretrizes para desenvolvimento JavaScript. O que pode ser encontrado em:
- Guia JavaScript Google
- Guia JavaScript Mozilla
- Guia JavaScript Github
- Guia JavaScript Douglas Crockford
Para o desenvolvimento usando o AngularJS é recomendado o Guia JavaScript Google.
Na wiki do AngularJS no Github temos uma seção similar feita pelo ProLoser, você pode vê-la aqui.
#Índice
#Geral
##Estrutura de Diretório
Uma vez que uma grande aplicação AngularJS tem muitos componentes, é melhor estruturá-la em uma hierarquia de diretórios. Há duas abordagens:
- Criando uma divisão alto nível por tipos de componentes e uma divisão baixo nível por funcionalidade.
Desta maneira a estrutura do diretório irá se parecer com:
.
├── app
│ ├── app.js
│ ├── controllers
│ │ ├── page1
│ │ │ ├── FirstCtrl.js
│ │ │ └── SecondCtrl.js
│ │ └── page2
│ │ └── ThirdCtrl.js
│ ├── directives
│ │ ├── page1
│ │ │ └── directive1.js
│ │ └── page2
│ │ ├── directive2.js
│ │ └── directive3.js
│ ├── filters
│ │ ├── page1
│ │ └── page2
│ └── services
│ ├── CommonService.js
│ ├── cache
│ │ ├── Cache1.js
│ │ └── Cache2.js
│ └── models
│ ├── Model1.js
│ └── Model2.js
├── lib
└── test
- Criando uma divisão alto nível por funcionalidade e baixo nível por tipos de componentes.
Aqui está seu modelo:
.
├── app
│ ├── app.js
│ ├── common
│ │ ├── controllers
│ │ ├── directives
│ │ ├── filters
│ │ └── services
│ ├── page1
│ │ ├── controllers
│ │ │ ├── FirstCtrl.js
│ │ │ └── SecondCtrl.js
│ │ ├── directives
│ │ │ └── directive1.js
│ │ ├── filters
│ │ │ ├── filter1.js
│ │ │ └── filter2.js
│ │ └── services
│ │ ├── service1.js
│ │ └── service2.js
│ └── page2
│ ├── controllers
│ │ └── ThirdCtrl.js
│ ├── directives
│ │ ├── directive2.js
│ │ └── directive3.js
│ ├── filters
│ │ └── filter3.js
│ └── services
│ └── service3.js
├── lib
└── test
-
Quando criarmos uma diretiva, pode ser útil colocar todos os arquivos associados as diretivas (i.e. templates, arquivos CSS/SASS, JavaScript) em uma pasta única. Se você escolher usar este estilo, seja consistente e use-o em todo seu projeto.
app └── directives ├── directive1 │ ├── directive1.html │ ├── directive1.js │ └── directive1.sass └── directive2 ├── directive2.html ├── directive2.js └── directive2.sass
Esta abordagem pode ser combinada com ambas as estruturas de diretórios acima.
- O arquivo
app.js
contém definição de rotas, configurações e/ou inicializações manuais (se necessário). - Cada arquivo JavaScript deve conter apenas um componente. O arquivo deve ser nomeado com o nome do componente.
- Use estruturas de projeto Angular como Yeoman ou ng-boilerplate.
Eu prefiro a primeira estrutura porque ela cria componentes comuns e fáceis de se achar.
Convenções sobre nomeação de componentes podem ser achadas em cada seção do componente.
##Otimizando o ciclo digest
- Observe somente as variáveis vitais (por exemplo: quando se usar comunicação em tempo real, não utilize um loop digest para cada mensagem recebida).
- Faça cálculos em
$watch
o mais simples que puder. Fazer cálculos pesados e lentos em um simples$watch
irá atrasar toda a aplicação (o loop $digest é feito em 'single thread' por causa da natureza 'single thread' do JavaScript).
##Outros
- Use:
$timeout
ao invés desetTimeout
$interval
ao invés desetInterval
$window
ao invés dewindow
$document
ao invés dedocument
$http
ao invés de$.ajax
Isto fará seus testes mais fáceis e em certos casos irá prevenir comportamentos inesperados (por exemplo, se você perder $scope.$apply
em setTimeout
).
-
Automatize seu fluxo de trabalho utilizando ferramentas como:
-
Use promises (
$q
) ao invés de callbacks. Isso tornará seu código mais elegante e limpo, e o salvará do inferno de callbacks. -
Use
$resource
ao invés de$http
quando possível. Um alto nível de abstração irá lhe salvar de redundância. -
Use um pré-minificador AngularJS (como ngmin ou ng-annotate) para prevenir problemas depois da minificação.
-
Não use globais. Resolva todas as dependências usando a Injeção de Dependências.
-
Não polua seu
$scope
. Somente adicione funções e variáveis que irão ser usadas nos templates.
#Módulos
Há duas maneiras comuns de se estruturar os módulos:
- Por funcionalidade
- Por tipo de componente
Atualmente não há uma grande diferença, mas a primeira forma parece mais limpa. Também, se os módulos 'lazy-loading' forem implementados (fora do roteiro AngularJS atualmente), isso irá melhorar a performance da sua aplicação.
#Controladores
-
Não manipule DOM nos controladores. Use diretivas para isso.
-
O nome do controlador é dado pela sua funcionalidade (por exemplo shopping cart, homepage, admin panel) e o adicional
Ctrl
no final. Os controladores são nomeados no formato UpperCamelCase (HomePageCtrl
,ShoppingCartCtrl
,AdminPanelCtrl
, etc.). -
Os controladores não devem ser definidos como globais (não importa que AngularJS permita isso, é uma má pratica pois polui o namespace).
-
Use a sintaxe de array para as definições do controlador:
module.controller('MyCtrl', ['dependency1', 'dependency2', ..., 'dependencyn', function (dependency1, dependency2, ..., dependencyn) { //...body }]);
Use este tipo de definição para evitar problemas com minificação. Você pode gerar automaticamente o array de definição a partir de um padrão usando ferramentas como ng-annotate (e uma tarefa grunt grunt-ng-annotate).
-
Use os nomes originais das dependências dos controladores. Isso irá ajudá-lo a produzir um código mais legível:
module.controller('MyCtrl', ['$scope', function (s) { //...body }]);
é menos legível que:
module.controller('MyCtrl', ['$scope', function ($scope) {
//...body
}]);
Isso é especialmente aplicado a um arquivo que contenha muito código que se faça necessário rolar (usar o scroll) por ele. Pois é provável que você esqueça qual variável está amarrada em qual dependência.
-
Faça os controladores o mais enxuto possível. Resuma as funções normalmente usadas no serviço.
-
Comunique entre controladores diversos usando o método de invocação (possível quando os elementos filhos querem se comunicar com os pais) ou
$emit
,$broadcast
e$on
métodos. As mensagens emitidas ($emit) e transmitidas ($broadcast) devem ser mantidas minimamente. -
Faça uma lista de todas as mensagens que são passadas usando
$emit
,$broadcast
e administre-as cuidadosamente por causa da coalisão de nomes e possíveis erros. -
Quando você precisar formatar dados, encapsule a lógica de formatação em um filtro e declare isso como uma dependência:
module.filter('myFormat', function () { return function () { //body... }; }); module.controller('MyCtrl', ['$scope', 'myFormatFilter', function ($scope, myFormatFilter) { //body... }]);
#Diretivas
- Nomeie suas diretivas no padrão lowerCamelCase
- Use
scope
ao invés de$scope
na sua função de link. Na compilação, pós/pre funções link que você tenha, definem os argumentos que irão ser passados quando a função é invocada, você não será capaz de muda-los usando DI (injeção de dependências). Este modelo também é usado no código fonte do AngularJS. - Use prefixos customizados para suas diretivas para previnir colisões de nomes com bibliotecas de terceiros.
- Não use
ng
ouui
prefixos pois eles estão reservados para o uso do AngularJS e AngularJS UI. - Manipulações DOM devem ser feitas somente através de diretivas.
- Crie um escopo isolado/independente quando você for desenvolver componentes reutilizáveis.
#Filtros
- Nomeie seus filtros no padrão lowerCamelCase
- Faça seus filtros o mais leve possível. Eles são chamados frequentemente durante o loop
$digest
então criando filtros lentos você irá atrasar sua aplicação.
#Serviços
- Use camelCase (lower ou upper) para nomear os serviços.
- Encapsule as 'business logic' nos services.
- Serviços encapsulando 'business logic' são preferencialmente um
service
ao invez de umfactory
- Para cachear a 'session-level' você pode usar
$cacheFactory
. Isto deve ser usado para cachear resultados de requisições ou computações pesadas.
#Templates
- Use
ng-bind
oung-cloak
ao invés de simplesmente{{ }}
para prevenir conteúdo piscando. - Evite escrever código complexo no template.
#Roteamento
- Use
resolve
para solucionar as dependências antes que o 'view' seja mostrado.