Meilleures pratiques AngularJS pour la déclaration de module?

115

J'ai un tas de modules angulaires déclarés dans mon application. J'ai commencé à les déclarer en utilisant la syntaxe «chaînée» comme ceci:

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

Mais j'ai décidé que ce n'était pas très facile à lire, alors j'ai commencé à les déclarer en utilisant une variable de module comme celle-ci:

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

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

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

La deuxième syntaxe me semble beaucoup plus lisible, mais mon seul reproche est que cette syntaxe laisse la modvariable de côté dans la portée globale. Si jamais j'avais une autre variable nommée mod, elle serait remplacée par la suivante (et d'autres problèmes associés aux variables globales).

Ma question est donc la suivante: est-ce le meilleur moyen? Ou serait-il préférable de faire quelque chose comme ça?:

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

Ou est-ce assez important de s'en soucier? Juste curieux de savoir quelles sont les "meilleures pratiques" pour la déclaration de module. Merci d'avance.

tennisgent
la source
3
Je me demandais la même chose à propos des meilleures pratiques angulaires
Dalorzo

Réponses:

118

'Meilleure' façon de déclarer un module

Comme angular est sur la portée globale elle-même et que les modules sont enregistrés dans sa variable, vous pouvez accéder aux modules 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){
      //..
  }])
})();

Aucune autre variable globale n'est requise.

Bien sûr, tout dépend des préférences, mais je pense que c'est en quelque sorte la meilleure pratique, car

  1. vous n'avez pas à polluer la portée mondiale
  2. vous pouvez accéder à vos modules partout et les trier ainsi que leurs fonctions dans différents fichiers à volonté
  3. vous pouvez utiliser la forme-fonction de "use strict";
  4. l'ordre de chargement des fichiers n'a pas autant d'importance

Options de tri de vos modules et fichiers

Cette façon de déclarer et d'accéder aux modules vous rend très flexible. Vous pouvez trier les modules par type de fonction (comme décrit dans une autre réponse) ou par route, par exemple:

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

La façon dont vous le triez à la fin est une question de goût personnel, de l'échelle et du type du projet. J'aime personnellement regrouper tous les fichiers d'un module dans le même dossier (classés dans des sous-dossiers de directives, de contrôleurs, de services et de filtres), y compris tous les différents fichiers de test, car cela rend vos modules plus réutilisables. Ainsi, dans les projets de taille moyenne, je me retrouve avec un module de base, qui comprend toutes les routes de base et leurs contrôleurs, services, directives et sous-modules plus ou moins complexes, alors que je pense qu'ils pourraient également être utiles pour d'autres projets, par exemple :

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

Pour les très gros projets, je finis parfois par regrouper les modules par routes, comme décrit ci-dessus ou par certaines routes principales sélectionnées ou même une combinaison de routes et de certains composants sélectionnés, mais cela dépend vraiment.

EDIT: Juste parce que c'est lié et que je suis tombé dessus très récemment: veillez à ne créer un module qu'une seule fois (en ajoutant un deuxième paramètre à la fonction angular.module). Cela gâchera votre application et peut être très difficile à détecter.

2015 EDIT sur les modules de tri: Un an et demi d'expérience angulaire plus tard, je peux ajouter que les avantages de l'utilisation de modules nommés différemment dans votre application sont quelque peu limités car AMD ne fonctionne toujours pas vraiment bien avec Angular et les services, directives et filtres sont globalement disponibles dans le contexte angulaire de toute façon ( comme illustré ici ). Cependant, il y a toujours un avantage sémantique et structurel et il pourrait être utile de pouvoir inclure / exclure un module avec une seule ligne de code commentée dans ou hors.

Il n'est également presque jamais très logique de séparer les sous-modules par type (par exemple, «myMapSubModule.controllers») car ils dépendent généralement les uns des autres.

hugo der hungrige
la source
7
Vous n'avez pas besoin de l'IIFE (expression de fonction immédiatement invoquée), alias fonction auto
exécutable
1
Vous avez raison. Vous n'en avez besoin que lorsque vous souhaitez appliquer la forme-fonction de "use strict"; Cela ne fait pas mal cependant.
hugo der hungrige
1
Dans la plupart des cas, vous pouvez également insérer 'use strict';votre composant. module.controller(function () { 'use strict'; ... });
Jackson
J'aime la mise en œuvre, mais je ne suis pas non plus un amusant à enchaîner, alors je mélange cela à ce que fait Beterraba
Mirko
1
lors de l'utilisation d'AMD, un seul module nommé app suffit. On peut exclure un module AMD en supprimant l'instruction require. Et nous n'avons plus besoin d'enregistrer les contrôleurs et les services.
James
28

J'adore le guide de style angulaire de Johnpapa, et voici quelques règles liées à cette question:

Règle: fonctions nommées vs fonctions anonymes

Évitez d'utiliser des fonctions anonymes:

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

À la place, utilisez des fonctions nommées:

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

function Dashboard() { }

Comme le dit l'auteur: This produces more readable code, is much easier to debug, and reduces the amount of nested callback code.

Règle: définissez 1 composant par fichier.

Évitez plusieurs composants dans un seul fichier:

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

function SomeController() { }

function someFactory() { }

Au lieu de cela, utilisez un fichier pour définir le module:

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

un fichier utilise simplement le module pour définir un composant

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

function SomeController() { }

et un autre fichier pour définir un autre composant

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

function someFactory() { }

Bien sûr, il existe de nombreuses autres règles pour les modules, les contrôleurs et les services qui sont assez utiles et méritent d'être lues.

Et grâce au commentaire de ya_dimon, le code ci-dessus devrait être enveloppé dans IIFE, par exemple:

(function (window, angular) {
  angular.module('app')
   .controller('Dashboard', function () { });
})(window, window.angular);
Aqingsao
la source
Bonne réponse et bon lien.
Ellesedil
si j'ai différents contrôleurs dans différents fichiers javascript, cela ne nécessitera-t-il pas le chargement d'un plus grand nombre de fichiers, c'est-à-dire plus de hits de serveur?
Vignesh Subramanian
Il est assez facile de les fusionner / uglifier / renommer avec gulp ou grunt, vignesh, et personnellement j'adore gulp.
aqingsao
1
vous avez oublié d'ajouter que tous ces extraits devraient être dans IIFE, sinon vous avez globalement des fonctions comme "someFactory ()". Il y a un risque de collision de noms. (et vous n'avez pas besoin de IIFE en es6)
ya_dimon
12

J'ai récemment eu cette énigme aussi. J'avais commencé comme vous en utilisant la syntaxe chaînée, mais à long terme, cela devient compliqué avec de grands projets. Normalement, je créerais un module de contrôleurs, un module de services et ainsi de suite dans des fichiers séparés et les injecterais dans mon module d'application principal trouvé dans un autre fichier. Par exemple:

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

Mais chacun de ces fichiers devenait beaucoup trop volumineux à mesure que le projet se développait. J'ai donc décidé de les diviser en fichiers séparés en fonction de chaque contrôleur ou service. J'ai trouvé que l'utilisation angular.module('mod-name').sans le tableau d'injection est ce dont vous avez besoin pour que cela fonctionne. Déclarer une variable globale dans un fichier et s'attendre à ce qu'elle soit facilement disponible dans un autre ne fonctionne tout simplement pas ou pourrait avoir des résultats inattendus.

Donc, en bref, mon application ressemblait à ceci:

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

J'ai également fait cela pour le fichier de services, pas besoin de changer le fichier du module d'application principal, vous injecteriez toujours les mêmes modules dans celui-ci.

Meconroy
la source
1
quel est l'intérêt de créer des modules séparés pour les services / directives / contrôleurs?
Filip Sobczak
2
Dans les grands projets, les choses peuvent être difficiles à trouver lorsque les contrôleurs / filtres / directives / services sont tous entrelacés. C'est juste une façon de garder les choses organisées.
meconroy
1
@FilipSobczak Il ne crée PAS de modules séparés pour les services / directives / contrôleurs. Au contraire, il n'a créé le module qu'une seule fois en utilisant angular.module('my-controllers',[]);(Notez qu'il ne spécifie le [] qu'une seule fois pour la déclaration). Il le réutilise simplement dans les autres fichiers. La séparation des fichiers rend relativement facile la maintenance du projet, en particulier les gros.
Devner
8

Une autre pratique consiste à remplir les contrôleurs, les directives, etc. dans leurs propres modules et à injecter ces modules dans votre module "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'
]);

Rien n'est laissé dans la portée globale.

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

Manny D
la source
Pourquoi utilisez-vous app.controllersinsted of controllerscomme nom de module, y a-t-il un avantage? Je suis un nouveau venu dans Angularjs
sijo vijayan
4

J'aime diviser mes fichiers et mes modules.

Quelque chose comme ça:

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

Je ne suis pas un grand fan du "style enchaîné", donc je préfère toujours écrire ma variable.

Beterraba
la source
2
C'est comme ça que je le faisais, mais chaque fichier services.js ou controller.js devient rapidement gros dans un projet à grande échelle, vous devrez éventuellement diviser chaque service ou contrôleur dans un fichier séparé.
meconroy
1
@meconroy Exactement. Quand la chose devient de plus en plus grande, j'aime décomposer la directive en modules plus petits et l'injecter ensuite dans le module directive "principal".
Beterraba
1

Je suggère de suivre le guide de style Angularjs .
Ils gèrent tous les concepts, de la convention de dénomination à la modularisation de votre application, etc.

Pour angulaire 2, vous pouvez consulter le guide de style angulaire 2

user3444693
la source
0

Pour moi, le chaînage est le moyen le plus compact:

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 cette façon, je peux facilement déplacer des composants entre les modules, je n'ai jamais besoin de déclarer le même module deux fois, je n'ai jamais besoin de variables globales.

Et si le fichier devient trop long, la solution est simple: diviser en deux fichiers, chacun déclarant son propre module en haut. Pour plus de transparence, j'essaie de conserver un module unique par fichier et de le nommer en ressemblant au chemin complet du fichier. De cette façon aussi, je n'ai jamais besoin d'écrire un module sans [], ce qui est un problème courant.

Dmitri Zaitsev
la source