¿Las mejores prácticas de AngularJS para la declaración del módulo?

115

Tengo un montón de módulos angulares declarados en mi aplicación. Originalmente comencé a declararlos usando la sintaxis "encadenada" como esta:

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

Pero decidí que no era muy fácil de leer, así que comencé a declararlos usando una variable de módulo como esta:

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

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

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

La segunda sintaxis me parece mucho más legible, pero mi única queja es que esta sintaxis deja la modvariable fuera del alcance global. Si alguna vez tengo alguna otra variable nombrada mod, se anulará con la siguiente (y otros problemas asociados con las variables globales).

Entonces mi pregunta es, ¿es esta la mejor manera? ¿O sería mejor hacer algo como esto ?:

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

¿O es que importa lo suficiente como para preocuparse? Solo tengo curiosidad por saber cuáles son las "mejores prácticas" para la declaración de módulos. Gracias por adelantado.

tenista
fuente
3
Me pregunté lo mismo sobre las mejores prácticas de Angular
Dalorzo

Respuestas:

118

'Mejor' forma de declarar un módulo

Como angular está en el alcance global y los módulos se guardan en su variable, puede acceder a los módulos a través de 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){
      //..
  }])
})();

No se requieren otras variables globales.

Por supuesto, todo depende de las preferencias, pero creo que esta es la mejor práctica, ya que

  1. no tienes que contaminar el alcance global
  2. puede acceder a sus módulos en todas partes y ordenarlos y sus funciones en diferentes archivos a voluntad
  3. puede usar la forma de función de "use estricto";
  4. el orden de carga de los archivos no importa tanto

Opciones para ordenar sus módulos y archivos

Esta forma de declarar y acceder a los módulos te hace muy flexible. Puede ordenar los módulos por tipo de función (como se describe en otra respuesta) o por ruta, por ejemplo:

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

La forma en que lo clasifique al final es una cuestión de gusto personal y la escala y el tipo de proyecto. Personalmente, me gusta agrupar todos los archivos de un módulo dentro de la misma carpeta (ordenados en subcarpetas de directivas, controladores, servicios y filtros), incluidos todos los archivos de prueba diferentes, ya que hace que sus módulos sean más reutilizables. Así, en proyectos de tamaño medio termino con un módulo base, que incluye todas las rutas básicas y sus controladores, servicios, directivas y submódulos más o menos complejos, cuando creo que también podrían ser útiles para otros proyectos, p. Ej. :

/******** 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 proyectos muy grandes, a veces termino agrupando módulos por rutas, como se describió anteriormente o por algunas rutas principales seleccionadas o incluso una combinación de rutas y algunos componentes seleccionados, pero realmente depende.

EDITAR: Solo porque está relacionado y me encontré con eso muy recientemente nuevamente: tenga mucho cuidado de crear un módulo solo una vez (agregando un segundo parámetro a la función angular.module). Esto estropeará su aplicación y puede ser muy difícil de detectar.

EDITAR 2015 sobre módulos de clasificación: un año y medio de experiencia angular más tarde, puedo agregar que los beneficios de usar módulos con nombres diferentes dentro de su aplicación son algo limitados ya que AMD todavía no funciona bien con Angular y servicios, directivas y filtros están disponibles globalmente dentro del contexto angular de todos modos ( como se ejemplifica aquí ). Sin embargo, todavía hay un beneficio semántico y estructural y podría ser útil poder incluir / excluir un módulo con una sola línea de código comentada dentro o fuera.

También casi nunca tiene mucho sentido separar submódulos por tipo (por ejemplo, 'myMapSubModule.controllers') ya que generalmente dependen unos de otros.

hugo der hungrige
fuente
7
No necesita el IIFE (Expresión de función invocada inmediatamente), también conocida como función anónima de ejecución automática
además de
1
Tienes razón. Solo lo necesita cuando desea aplicar la forma de función de "use estricto"; Aunque no duele.
hugo der hungrige
1
En la mayoría de los casos, también puede colocar 'use strict';su componente. module.controller(function () { 'use strict'; ... });
Jackson
Me gusta la implementación, pero tampoco me gusta encadenar, así que estoy mezclando esto con lo que está haciendo Beterraba
Mirko
1
cuando se usa AMD, un solo módulo llamado aplicación es suficiente. Se puede excluir un módulo AMD eliminando la declaración require. Y ya no necesitamos registrar los controladores y servicios.
James
28

Me encanta la guía de estilo angular de Johnpapa, y aquí hay algunas reglas relacionadas con esta pregunta:

Regla: funciones nombradas vs anónimas

Evite el uso de funciones anónimas:

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

En su lugar, use funciones con nombre:

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

function Dashboard() { }

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

Regla: Defina 1 componente por archivo.

Evite varios componentes en un archivo:

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

function SomeController() { }

function someFactory() { }

En cambio, use un archivo para definir el módulo:

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

un archivo solo usa el módulo para definir un componente

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

function SomeController() { }

y otro archivo para definir otro componente

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

function someFactory() { }

Por supuesto, hay muchas otras reglas para módulos, controladores y servicios que son bastante útiles y que vale la pena leer.

Y gracias al comentario de ya_dimon, el código anterior debería estar envuelto en IIFE, por ejemplo:

(function (window, angular) {
  angular.module('app')
   .controller('Dashboard', function () { });
})(window, window.angular);
aqingsao
fuente
Buena respuesta y gran enlace.
Ellesedil
si tengo diferentes controladores en diferentes archivos javascript, ¿no será necesario cargar más archivos, es decir, más visitas al servidor?
Vignesh Subramanian
Es bastante fácil fusionarlos / uglify / renombrarlos con gulp o gruñido, vignesh, y personalmente me encanta gulp.
aqingsao
1
olvidó agregar que todos estos fragmentos deben estar en IIFE, de lo contrario, tiene funciones como "someFactory ()" globalmente. Existe la posibilidad de colisiones de nombres. (y no necesitas IIFE en es6)
ya_dimon
12

Recientemente también tuve este acertijo. Comencé como tú usando la sintaxis encadenada, pero a la larga se vuelve difícil de manejar con proyectos grandes. Normalmente, crearía un módulo de controladores, un módulo de servicios, etc. en archivos separados y los inyectaría en mi módulo de aplicación principal que se encuentra en otro archivo. Por ejemplo:

// 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',...]);

Pero cada uno de estos archivos se estaba volviendo demasiado grande a medida que crecía el proyecto. Así que decidí dividirlos en archivos separados según cada controlador o servicio. Descubrí que usar angular.module('mod-name').sin la matriz de inyección es lo que necesita para que esto funcione. Declarar una variable global en un archivo y esperar que esté disponible en otro simplemente no funciona o podría tener resultados inesperados.

En resumen, mi aplicación se veía así:

// 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',[...]);

También hice esto en el archivo de servicios, no es necesario cambiar el archivo del módulo de la aplicación principal, todavía estaría inyectando los mismos módulos en eso.

meconroy
fuente
1
¿Cuál es el punto de crear módulos separados para servicios / directivas / controladores?
Filip Sobczak
2
En proyectos grandes, las cosas pueden ser difíciles de encontrar cuando los controladores / filtros / directivas / servicios están todos intercalados. Esta es solo una forma de mantener las cosas organizadas.
meconroy
1
@FilipSobczak NO está creando módulos separados para servicios / directivas / controladores. Más bien, ha creado el módulo solo una vez usando angular.module('my-controllers',[]);(Tenga en cuenta que está especificando [] solo una vez para la declaración). Simplemente está reutilizando esto en los otros archivos. La separación de archivos hace que sea relativamente fácil mantener el proyecto, especialmente los grandes.
Devner
8

Otra práctica es rellenar controladores, directivas, etc. en sus propios módulos e inyectar esos módulos en el "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'
]);

No queda nada en el ámbito global.

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

Manny D
fuente
¿Por qué utiliza app.controllersinsted of controllerscomo nombre del módulo? ¿Hay alguna ventaja? Soy un recién llegado a Angularjs
sijo vijayan
4

Me gusta dividir mis archivos y mis módulos.

Algo como esto:

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( /* ... */ );

No soy un gran admirador del "estilo encadenado", así que prefiero anotar mi variable siempre.

Beterraba
fuente
2
Esta es la forma en que lo había estado haciendo, pero, cada archivo services.js o controller.js crece rápidamente en un proyecto a gran escala, eventualmente necesitará dividir cada servicio o controlador en un archivo separado.
meconroy
1
@meconroy Exactamente. Cuando la cosa se vuelve más y más grande, me gusta dividir la directiva en módulos más pequeños e inyectarla en el módulo de directiva "principal".
Beterraba
0

Para mí, el encadenamiento es la forma más 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 = ...;
 })
 ;

De esa manera puedo mover fácilmente componentes entre módulos, nunca necesito declarar el mismo módulo dos veces, nunca necesito variables globales.

Y si el archivo se vuelve demasiado largo, la solución es simple: dividir en dos archivos, cada uno declarando su propio módulo en la parte superior. Para mayor transparencia, trato de mantener un módulo único por archivo y nombrarlo que se parezca a la ruta completa del archivo. De esta manera tampoco necesito escribir un módulo sin él [], lo cual es un punto débil común.

Dmitri Zaitsev
fuente