Melhores práticas do AngularJS para declaração de módulo?

115

Eu tenho um monte de módulos angulares declarados em meu aplicativo. Eu originalmente comecei a declará-los usando a sintaxe "encadeada" como esta:

angular.module('mymodule', [])
    .controller('myctrl', ['dep1', function(dep1){ ... }])
    .service('myservice', ['dep2', function(dep2){ ... }])
    ... // more here

Mas decidi que não era muito fácil de ler, então comecei a declará-los usando uma variável de módulo como esta:

var mod = angular.module('mymodule', []);

mod.controller('myctrl', ['dep1', function(dep1){ ... }]);

mod.service('myservice', ['dep2', function(dep2){ ... }]);
...

A segunda sintaxe parece muito mais legível para mim, mas minha única reclamação é que essa sintaxe deixa a modvariável de fora no escopo global. Se algum dia eu tiver alguma outra variável nomeada mod, ela será substituída pela próxima (e outros problemas associados às variáveis ​​globais).

Então, minha pergunta é: essa é a melhor maneira? Ou seria melhor fazer algo assim ?:

(function(){
    var mod = angular.module('mymod', []);
    mod.controller('myctrl', ['dep1', function(dep1){ ... }]);
    mod.service('myservice', ['dep2', function(dep2){ ... }]);
    ...
})();

Ou isso importa o suficiente para se importar? Apenas curioso para saber quais são as "melhores práticas" para declaração de módulo. Desde já, obrigado.

tenista
fonte
3
Eu me perguntei o mesmo sobre as melhores práticas angulares
Dalorzo

Respostas:

118

'Melhor' maneira de declarar um módulo

Como o angular está no próprio escopo global e os módulos são salvos em sua variável, você pode acessar os módulos via angular.module('mymod'):

// one file
// NOTE: the immediately invoked function expression 
// is used to exemplify different files and is not required
(function(){
   // declaring the module in one file / anonymous function
   // (only pass a second parameter THIS ONE TIME as a redecleration creates bugs
   // which are very hard to dedect)
   angular.module('mymod', []);
})();


// another file and/or another anonymous function
(function(){   
 // using the function form of use-strict...
 "use strict";
  // accessing the module in another. 
  // this can be done by calling angular.module without the []-brackets
  angular.module('mymod')
    .controller('myctrl', ['dep1', function(dep1){
      //..
    }])

  // appending another service/controller/filter etc to the same module-call inside the same file
    .service('myservice', ['dep2', function(dep2){ 
    //... 
    }]);

  // you can of course use angular.module('mymod') here as well
  angular.module('mymod').controller('anothermyctrl', ['dep1', function(dep1){
      //..
  }])
})();

Nenhuma outra variável global é necessária.

Claro que depende tudo das preferências, mas acho que essa é a melhor prática, pois

  1. você não tem que poluir o escopo global
  2. você pode acessar seus módulos em qualquer lugar e classificá-los e suas funções em diferentes arquivos à vontade
  3. você pode usar a forma de função "use strict";
  4. a ordem de carregamento dos arquivos não importa tanto

Opções para classificar seus módulos e arquivos

Esta forma de declarar e acessar os módulos torna você muito flexível. Você pode classificar os módulos via tipo de função (como descrito em outra resposta) ou via rota, por exemplo:

/******** sorting by route **********/    
angular.module('home')...
angular.module('another-route')...
angular.module('shared')...

Como você classifica isso no final é uma questão de gosto pessoal e da escala e tipo do projeto. Eu pessoalmente gosto de agrupar todos os arquivos de um módulo dentro da mesma pasta (ordenados em subpastas de diretivas, controladores, serviços e filtros), incluindo todos os arquivos de teste diferentes, pois torna seus módulos mais reutilizáveis. Assim, em projetos de médio porte acabo com um módulo-base, que inclui todas as rotas básicas e seus controladores, serviços, diretivas e submódulos mais ou menos complexos, quando acho que poderiam ser úteis para outros projetos também, por exemplo :

/******** modularizing feature-sets **********/
/controllers
/directives
/filters
/services
/my-map-sub-module
/my-map-sub-module/controllers
/my-map-sub-module/services
app.js
...

angular.module('app', [
  'app.directives',
  'app.filters',
  'app.controllers',
  'app.services',
  'myMapSubModule'
]);

angular.module('myMapSubModule',[
   'myMapSubModule.controllers',
   'myMapSubModule.services',
   // only if they are specific to the module
   'myMapSubModule.directives',
   'myMapSubModule.filters'
]);

Para projetos muito grandes, às vezes acabo agrupando módulos por rotas, como descrito acima ou por algumas rotas principais selecionadas ou mesmo uma combinação de rotas e alguns componentes selecionados, mas realmente depende.

EDIT: Só porque está relacionado e me deparei com isso muito recentemente: Tome cuidado para criar um módulo apenas uma vez (adicionando um segundo parâmetro à função angular.module). Isso bagunçará seu aplicativo e pode ser muito difícil de detectar.

EDITAR 2015 sobre módulos de classificação: um ano e meio de experiência angular depois, posso acrescentar que os benefícios de usar módulos com nomes diferentes em seu aplicativo são um tanto limitados, pois a AMD ainda não funciona bem com Angular e serviços, diretivas e filtros estão globalmente disponíveis dentro do contexto angular de qualquer maneira ( conforme exemplificado aqui ). No entanto, ainda há um benefício semântico e estrutural e pode ser útil ser capaz de incluir / excluir um módulo com uma única linha de código comentada dentro ou fora.

Também quase nunca faz muito sentido separar submódulos por tipo (por exemplo, 'myMapSubModule.controllers'), pois geralmente dependem uns dos outros.

hugo der hungrige
fonte
7
Você não precisa do IIFE (Imediatamente Chamado Função Expression), a função de executar auto aka anónimo
mais-
1
Você está certo. Você só precisa disso quando quiser aplicar a forma de função "use strict"; Mas não dói.
hugo der hungrige
1
Na maioria dos casos, você também pode colocar 'use strict';dentro do seu componente. module.controller(function () { 'use strict'; ... });
Jackson
Eu gosto de implementação, mas também não gosto de encadear, então estou misturando isso com o que Beterraba está fazendo
Mirko
1
ao usar AMD, um único módulo chamado app é suficiente. Pode-se excluir um módulo AMD excluindo a instrução require. E não precisamos mais registrar os controladores e serviços.
James,
28

Eu amo o guia de estilo angular de Johnpapa, e aqui estão algumas regras relacionadas a essa questão:

Regra: funções nomeadas versus funções anônimas

Evite usar funções anônimas:

// dashboard.js
angular
  .module('app')
  .controller('Dashboard', function() { })

Em vez disso, use funções nomeadas:

// dashboard.js
angular
  .module('app')
  .controller('Dashboard', Dashboard);

function Dashboard() { }

Como diz o autor: This produces more readable code, is much easier to debug, and reduces the amount of nested callback code.

Regra: Defina 1 componente por arquivo.

Evite vários componentes em um arquivo:

angular
  .module('app', ['ngRoute'])
  .controller('SomeController', SomeController)
  .factory('someFactory', someFactory);

function SomeController() { }

function someFactory() { }

Em seguida, use um arquivo para definir o módulo:

// app.module.js
angular
  .module('app', ['ngRoute']);

um arquivo apenas usa o módulo para definir um componente

// someController.js
angular
  .module('app')
  .controller('SomeController', SomeController);

function SomeController() { }

e outro arquivo para definir outro componente

// someFactory.js
angular
  .module('app')
  .factory('someFactory', someFactory);

function someFactory() { }

Claro, existem muitas outras regras para módulos, controladores e serviços que são bastante úteis e vale a pena ler.

E graças ao comentário de ya_dimon, o código acima deve ser encapsulado em IIFE, por exemplo:

(function (window, angular) {
  angular.module('app')
   .controller('Dashboard', function () { });
})(window, window.angular);
Aqingsao
fonte
Boa resposta e ótimo link.
Ellesedil
Se eu tiver controladores diferentes em arquivos javascript diferentes, não vai exigir o carregamento de mais arquivos, ou seja, mais acessos ao servidor?
Vignesh Subramanian
É muito fácil mesclá-los / modificá-los / renomeá-los com gulp ou grunt, vignesh e, pessoalmente, adoro gulp.
aqingsao 02 de
1
você se esqueceu de adicionar, que todos esses snippets devem estar no IIFE, caso contrário, você terá funções como "someFactory ()" globalmente. Existe uma chance de colisões de nomes. (e você não precisa do IIFE no es6)
ya_dimon
12

Recentemente, também tive esse enigma. Eu comecei exatamente como você usando a sintaxe encadeada, mas no longo prazo ela se torna difícil de manejar com projetos grandes. Normalmente, eu criaria um módulo de controladores, um módulo de serviços e assim por diante em arquivos separados e os injetaria em meu módulo de aplicativo principal encontrado em outro arquivo. Por exemplo:

// My Controllers File
angular.module('my-controllers',[])
    .controller('oneCtrl',[...])
    .controller('twoCtrl',[...]);

// My Services File
angular.module('my-services',[])
    .factory('oneSrc',[...])
    .facotry('twoSrc',[...]);

// My Directives File
angular.module('my-directives',[])
    .directive('oneDrct',[...])
    .directive('twoDrct',[...]);

// My Main Application File
angular.module('my-app',['my-controllers','my-services','my-directives',...]);

Mas cada um desses arquivos estava ficando grande à medida que o projeto crescia. Portanto, decidi dividi-los em arquivos separados com base em cada controlador ou serviço. Descobri que usar angular.module('mod-name').sem a matriz de injeção é o que você precisa para que isso funcione. Declarar uma variável global em um arquivo e esperar que ela esteja prontamente disponível em outro simplesmente não funciona ou pode ter resultados inesperados.

Resumindo, meu aplicativo era mais ou menos assim:

// Main Controller File
angular.module('my-controllers',[]);

// Controller One File
angular.module('my-controllers').controller('oneCtrl',[...]);

//Controller Two File
angular.module('my-controllers').controller('twoCtrl',[...]);

Eu fiz isso com o arquivo de serviços também, não há necessidade de alterar o arquivo do módulo do aplicativo principal, você ainda estaria injetando os mesmos módulos nele.

meconroy
fonte
1
qual é o ponto de criar módulos separados para serviços / diretivas / controladores?
Filip Sobczak
2
Em grandes projetos, as coisas podem ficar difíceis de encontrar quando os controladores / filtros / diretivas / serviços são intercalados. Essa é apenas uma maneira de manter as coisas organizadas.
meconroy,
1
@FilipSobczak Ele NÃO está criando módulos separados para serviços / diretivas / controladores. Em vez disso, ele criou o módulo apenas uma vez usando angular.module('my-controllers',[]);(observe que ele está especificando [] apenas uma vez para declaração). Ele está simplesmente reutilizando isso nos outros arquivos. A separação de arquivos torna relativamente fácil manter o projeto, especialmente os grandes.
Devner
8

Uma outra prática é colocar controladores, diretivas, etc. em seus próprios módulos e injetar esses módulos em seu módulo "principal":

angular.module('app.controllers', [])
  .controller('controller1', ['$scope', function (scope) {
    scope.name = "USER!";
  }]);

angular.module('app.directives', [])
  .directive('myDirective', [function () {
    return {
      restrict: 'A',
      template: '<div>my directive!</div>'
    }
  }]);

angular.module('app', [
  'app.controllers',
  'app.directives'
]);

Nada resta no âmbito global.

http://plnkr.co/edit/EtzzPRyxWT1MkhK7KcLo?p=preview

Manny D
fonte
Por que você está usando em app.controllersvez de controllerscomo o nome do módulo? Existe alguma vantagem? Sou um novato no Angularjs
sijo vijayan
4

Gosto de dividir meus arquivos e meus módulos.

Algo assim:

app.js

var myApp = angular.module('myApp', ['myApp.controllers', 'myApp.directives', 'myApp.services']);

myApp.config(['$routeProvider', function($routeProvider) {
    /* routes configs */
    $routeProvider.when(/*...*/);
}]);

directives.js

var myDirectives = angular.module('myApp.directives', []);

myDirectives.directive( /* ... */ );

service.js

var myServices = angular.module('myApp.services', []);

myServices.factory( /* ... */ );

Não sou um grande fã do "estilo encadeado", então prefiro sempre escrever minha variável.

Beterraba
fonte
2
Esta é a maneira que eu estava fazendo, mas, cada arquivo services.js ou controller.js fica grande rapidamente em um projeto de grande escala, eventualmente você precisará dividir cada serviço ou controlador em um arquivo separado.
meconroy
1
@meconroy Exatamente. Quando a coisa fica cada vez maior, gosto de dividir a diretiva em módulos menores e injetá-la no módulo de diretiva "principal".
Beterraba
1

Eu sugiro seguir o Guia de Estilo Angularjs .
Eles lidam com todo o conceito, desde a convenção de nomenclatura até modularizar seu aplicativo e assim por diante.

Para angular 2, você pode verificar o Guia de estilo Angular 2

user3444693
fonte
0

Para mim, o encadeamento é a forma mais compacta:

angular.module("mod1",["mod1.submod1"])

 .value("myValues", {
   ...
 })

 .factory("myFactory", function(myValues){
   ...
 })

 .controller("MainCtrl", function($scope){

   // when using "Ctrl as" syntax
   var MC = this;
   MC.data = ...;
 })
 ;

Dessa forma, posso mover facilmente componentes entre módulos, nunca preciso declarar o mesmo módulo duas vezes, nunca preciso de nenhuma variável global.

E se o arquivo ficar muito longo, a solução é simples - divida em dois arquivos, cada um declarando seu próprio módulo no topo. Para obter mais transparência, tento manter um módulo exclusivo por arquivo e nomeá-lo semelhante ao caminho completo do arquivo. Dessa forma, também nunca preciso escrever um módulo sem [], o que é um ponto problemático comum.

Dmitri Zaitsev
fonte