Migliori pratiche di AngularJS per la dichiarazione del modulo?


115

Ho un sacco di moduli Angular dichiarati nella mia app. Inizialmente ho iniziato a dichiararli usando la sintassi "concatenata" come questa:

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

Ma ho deciso che non era molto facile da leggere, quindi ho iniziato a dichiararli usando una variabile del modulo come questa:

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

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

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

La seconda sintassi mi sembra molto più leggibile, ma la mia unica lamentela è che questa sintassi modesclude la variabile nell'ambito globale. Se dovessi mai avere qualche altra variabile denominata mod, sarebbe sovrascritta con questa successiva (e altri problemi associati alle variabili globali).

Quindi la mia domanda è: è questo il modo migliore? O sarebbe meglio fare qualcosa del genere ?:

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

O importa abbastanza da preoccuparsene? Sono solo curioso di sapere quali sono le "migliori pratiche" per la dichiarazione dei moduli. Grazie in anticipo.


3
Mi chiedevo lo stesso di Angular Best Practices
Dalorzo

Risposte:


118

Il modo "migliore" per dichiarare un modulo

Poiché angular si trova sull'ambito globale stesso ei moduli vengono salvati nella sua variabile, è possibile accedere ai moduli tramite 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){
      //..
  }])
})();

Non sono necessarie altre variabili globali.

Ovviamente dipende tutto dalle preferenze, ma penso che questa sia una specie di best practice, come

  1. non devi inquinare l'ambito globale
  2. puoi accedere ai tuoi moduli ovunque e ordinarli e le loro funzioni in file diversi a piacimento
  3. puoi usare la forma-funzione di "use strict";
  4. l'ordine di caricamento dei file non è così importante

Opzioni per ordinare i tuoi moduli e file

Questo modo di dichiarare e accedere ai moduli ti rende molto flessibile. Puoi ordinare i moduli tramite il tipo di funzione (come descritto in un'altra risposta) o tramite route, ad esempio:

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

Il modo in cui lo ordinate alla fine è una questione di gusti personali, della scala e del tipo di progetto. Personalmente mi piace raggruppare tutti i file di un modulo all'interno della stessa cartella (ordinati in sottocartelle di direttive, controller, servizi e filtri), inclusi tutti i diversi file di test, in quanto rende i moduli più riutilizzabili. Quindi nei progetti di medie dimensioni mi ritrovo con un modulo di base, che include tutti i percorsi di base ei loro controller, servizi, direttive e sottomoduli più o meno complessi, quando penso che potrebbero essere utili anche per altri progetti, ad es. :

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

Per progetti molto grandi, a volte finisco per raggruppare i moduli per rotte, come descritto sopra o per alcune rotte principali selezionate o anche una combinazione di rotte e alcuni componenti selezionati, ma dipende davvero.

EDIT: Solo perché è correlato e mi sono imbattuto di nuovo in quello molto recentemente: fai molta attenzione a creare un modulo solo una volta (aggiungendo un secondo parametro alla funzione angular.module). Questo rovinerà la tua applicazione e può essere molto difficile da rilevare.

2015 MODIFICA sui moduli di ordinamento: un anno e mezzo di esperienza angolare dopo, posso aggiungere che i vantaggi derivanti dall'utilizzo di moduli con nomi diversi all'interno della tua app sono in qualche modo limitati in quanto AMD non funziona ancora bene con Angular e servizi, direttive e filtri sono comunque globalmente disponibili all'interno del contesto angolare ( come esemplificato qui ). C'è ancora un vantaggio semantico e strutturale e potrebbe essere utile poter includere / escludere un modulo con una singola riga di codice commentata dentro o fuori.

Inoltre, non ha quasi mai molto senso separare i sottomoduli in base al tipo (es. "MyMapSubModule.controllers") poiché di solito dipendono l'uno dall'altro.


7
Non hai bisogno dell'IIFE (Espressione di funzione immediatamente invocata), nota anche come funzione autoesecuzione anonima
più-

1
Hai ragione. Ne hai bisogno solo quando vuoi applicare la forma-funzione di "use strict"; Non fa male però.
hugo der hungrige

1
Nella maggior parte dei casi puoi inserire anche il 'use strict';tuo componente. module.controller(function () { 'use strict'; ... });
Jackson il

Mi piace l'implementazione, ma non mi piace nemmeno concatenare, quindi sto mescolando questo a quello che sta facendo Beterraba
Mirko,

1
quando si utilizza AMD, è sufficiente un singolo modulo denominato app. È possibile escludere un modulo AMD eliminando l'istruzione require. E non abbiamo più bisogno di registrare i controller e i servizi.
James il

28

Adoro la guida angolare di Johnpapa, e qui ci sono alcune regole relative a questa domanda:

Regola: funzioni con nome e funzioni anonime

Evita di utilizzare funzioni anonime:

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

Usa invece le funzioni denominate:

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

function Dashboard() { }

Come dice l'autore: This produces more readable code, is much easier to debug, and reduces the amount of nested callback code.

Regola: definire 1 componente per file.

Evita più componenti in un file:

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

function SomeController() { }

function someFactory() { }

Intead, usa un file per definire il modulo:

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

un file usa semplicemente il modulo per definire un componente

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

function SomeController() { }

e un altro file per definire un altro componente

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

function someFactory() { }

Naturalmente, ci sono molte altre regole per moduli, controller e servizi che sono abbastanza utili e che vale la pena leggere.

E grazie al commento di ya_dimon, il codice sopra dovrebbe essere racchiuso in IIFE, ad esempio:

(function (window, angular) {
  angular.module('app')
   .controller('Dashboard', function () { });
})(window, window.angular);

Buona risposta e ottimo collegamento.
Ellesedil

se ho controller diversi in file javascript diversi non sarà necessario caricare un numero maggiore di file, ovvero più hit del server?
Vignesh Subramanian

È abbastanza facile unirli / modificarli / rinominarli con gulp o grunt, vignesh e personalmente adoro gulp.
aqingsao

1
ti sei dimenticato di aggiungere che tutti questi frammenti dovrebbero essere in IIFE, altrimenti hai funzioni come "someFactory ()" a livello globale. È possibile che si verifichino conflitti di nome. (e non hai bisogno di IIFE in es6)
ya_dimon

12

Recentemente ho avuto anche questo enigma. Avevo iniziato proprio come te usando la sintassi concatenata, ma a lungo andare diventa ingombrante con grandi progetti. Normalmente creerei un modulo controller, un modulo servizi e così via in file separati e li inietterei nel modulo dell'applicazione principale che si trova in un altro file. Per esempio:

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

Ma ognuno di questi file diventava sempre più grande con la crescita del progetto. Quindi ho deciso di suddividerli in file separati in base a ciascun controller o servizio. Ho scoperto che l'uso angular.module('mod-name').senza l'array di iniezione è ciò di cui hai bisogno perché funzioni. Dichiarare una variabile globale in un file e aspettarsi che sia prontamente disponibile in un altro semplicemente non funziona o potrebbe avere risultati imprevisti.

Quindi, in breve, la mia applicazione era simile a questa:

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

L'ho fatto anche per il file dei servizi, non è necessario modificare il file del modulo dell'applicazione principale che avresti ancora iniettato gli stessi moduli in quello.


1
qual è il punto di creare moduli separati per servizi / direttive / controllori?
Filip Sobczak

2
In progetti di grandi dimensioni le cose possono diventare difficili da trovare quando controller / filtri / direttive / servizi sono tutti interlacciati insieme. Questo è solo un modo per mantenere le cose organizzate.
meconroy

1
@FilipSobczak NON sta creando moduli separati per servizi / direttive / controllori. Piuttosto, ha creato il modulo solo una volta usando angular.module('my-controllers',[]);(Notare che sta specificando [] solo una volta per la dichiarazione). Lo sta semplicemente riutilizzando negli altri file. La separazione dei file rende relativamente facile mantenere il progetto, specialmente quelli di grandi dimensioni.
Devner

8

Un'altra pratica è inserire controller, direttive, ecc. Nei propri moduli e iniettare quei moduli in quello "principale":

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

Nulla è lasciato nell'ambito globale.

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


Perché stai usando app.controllersinvece di controllerscome nome del modulo, c'è qualche vantaggio? Sono un nuovo arrivato ad Angularjs
sijo vijayan

4

Mi piace dividere i miei file e i miei moduli.

Qualcosa come questo:

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

Non sono un grande fan dello "stile concatenato", quindi preferisco scrivere sempre la mia variabile.


2
Questo è il modo in cui lo stavo facendo ma, ogni file services.js o controller.js diventa molto veloce in un progetto su larga scala, alla fine dovrai suddividere ogni servizio o controller in un file separato.
meconroy

1
@meconroy Esattamente. Quando la cosa diventa sempre più grande, mi piace suddividere la direttiva in moduli più piccoli e quindi iniettarla nel modulo direttiva "principale".
Beterraba


0

Per me, il concatenamento è il modo più compatto:

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

In questo modo posso spostare facilmente i componenti tra i moduli, non ho mai bisogno di dichiarare lo stesso modulo due volte, non ho mai bisogno di variabili globali.

E se il file diventa troppo lungo, la soluzione è semplice: dividi in due file, ognuno dei quali dichiara il proprio modulo all'inizio. Per una maggiore trasparenza, cerco di mantenere un modulo univoco per file e di assegnargli un nome simile al percorso completo del file. In questo modo inoltre non ho mai bisogno di scrivere un modulo senza [], che è un punto dolente comune.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.