Best Practices von AngularJS für die Moduldeklaration?


115

Ich habe eine Reihe von Angular-Modulen in meiner App deklariert. Ich habe ursprünglich damit begonnen, sie mit der "verketteten" Syntax wie folgt zu deklarieren:

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

Aber ich entschied, dass das nicht sehr einfach zu lesen war, und begann, sie mit einer Modulvariablen wie der folgenden zu deklarieren:

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

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

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

Die zweite Syntax scheint mir viel besser lesbar zu sein, aber meine einzige Beschwerde ist, dass diese Syntax die modVariable im globalen Bereich auslässt. Wenn ich jemals eine andere Variable benannt habe mod, wird diese mit der nächsten überschrieben (und anderen Problemen, die mit globalen Variablen verbunden sind).

Meine Frage ist also, ist das der beste Weg? Oder wäre es besser, so etwas zu tun?:

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

Oder ist es überhaupt wichtig genug, sich darum zu kümmern? Ich bin nur neugierig zu wissen, was die "Best Practices" für die Moduldeklaration sind. Danke im Voraus.


3
Ich fragte mich das gleiche über Angular Best Practices
Dalorzo

Antworten:


118

'Beste' Möglichkeit, ein Modul zu deklarieren

Da Angular im globalen Bereich selbst liegt und Module in seiner Variablen gespeichert werden, können Sie auf Module zugreifen über 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){
      //..
  }])
})();

Es sind keine weiteren globalen Variablen erforderlich.

Natürlich hängt alles von den Vorlieben ab, aber ich denke, dies ist eine Art Best Practice

  1. Sie müssen den globalen Geltungsbereich nicht verschmutzen
  2. Sie können überall auf Ihre Module zugreifen und sie und ihre Funktionen nach Belieben in verschiedene Dateien sortieren
  3. Sie können die Funktionsform "use strict" verwenden.
  4. Die Ladereihenfolge der Dateien spielt keine Rolle

Optionen zum Sortieren Ihrer Module und Dateien

Diese Art der Deklaration und des Zugriffs auf Module macht Sie sehr flexibel. Sie können Module nach Funktionstyp (wie in einer anderen Antwort beschrieben) oder nach Route sortieren, z.

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

Wie Sie es am Ende sortieren, hängt vom persönlichen Geschmack sowie vom Umfang und der Art des Projekts ab. Ich persönlich mag es, alle Dateien eines Moduls in demselben Ordner zu gruppieren (in Unterordnern von Anweisungen, Controllern, Diensten und Filtern angeordnet), einschließlich aller verschiedenen Testdateien, da Ihre Module dadurch wiederverwendbarer werden. So habe ich in mittelgroßen Projekten ein Basismodul, das alle grundlegenden Routen und ihre Controller, Dienste, Anweisungen und mehr oder weniger komplexen Untermodule enthält, wenn ich denke, dass sie auch für andere Projekte nützlich sein könnten, z ::

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

Bei sehr großen Projekten gruppiere ich Module manchmal nach Routen, wie oben beschrieben, oder nach ausgewählten Hauptrouten oder sogar nach einer Kombination von Routen und einigen ausgewählten Komponenten, aber das hängt wirklich davon ab.

EDIT: Nur weil es verwandt ist und ich kürzlich wieder darauf gestoßen bin: Achten Sie darauf, dass Sie ein Modul nur einmal erstellen (indem Sie der angular.module-Funktion einen zweiten Parameter hinzufügen). Dies wird Ihre Anwendung durcheinander bringen und kann sehr schwer zu erkennen sein.

2015 EDIT zum Sortieren von Modulen: Eineinhalb Jahre Erfahrung mit Winkeln später kann ich hinzufügen, dass die Vorteile der Verwendung von Modulen mit unterschiedlichen Namen in Ihrer App etwas begrenzt sind, da AMD mit Angular und Diensten, Anweisungen und Filtern immer noch nicht wirklich gut funktioniert sind ohnehin global im Winkelkontext verfügbar ( wie hier beispielhaft dargestellt ). Es gibt jedoch immer noch einen semantischen und strukturellen Vorteil, und es kann hilfreich sein, ein Modul mit einer einzelnen Codezeile ein- oder auszuschließen, die ein- oder ausgekommentiert wird.

Es ist auch fast nie sinnvoll, Untermodule nach Typ zu trennen (z. B. 'myMapSubModule.controllers'), da sie normalerweise voneinander abhängen.


7
Sie benötigen nicht den IIFE (sofort aufgerufenen Funktionsausdruck), auch bekannt als anonyme selbstausführende Funktion
plus

1
Du hast recht. Sie benötigen es nur, wenn Sie die Funktionsform "use strict" anwenden möchten. Tut aber nicht weh.
Hugo der Hungrige

1
In den meisten Fällen können Sie auch 'use strict';in Ihre Komponente einfügen. module.controller(function () { 'use strict'; ... });
Jackson

Ich mag die Implementierung, aber es macht mir auch keinen Spaß, sie zu verketten, also mische ich dies mit dem, was Beterraba tut
Mirko

1
Bei Verwendung von AMD reicht ein einziges Modul mit dem Namen app aus. Ein AMD-Modul kann durch Löschen der require-Anweisung ausgeschlossen werden. Und wir müssen die Controller und Dienste nicht mehr registrieren.
James

28

Ich liebe den Angular -Styleguide von Johnpapa, und hier sind einige Regeln, die sich auf diese Frage beziehen:

Regel: Benannte vs Anonyme Funktionen

Vermeiden Sie anonyme Funktionen:

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

Verwenden Sie stattdessen benannte Funktionen:

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

function Dashboard() { }

Wie der Autor sagt: This produces more readable code, is much easier to debug, and reduces the amount of nested callback code.

Regel: Definieren Sie 1 Komponente pro Datei.

Vermeiden Sie mehrere Komponenten in einer Datei:

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

function SomeController() { }

function someFactory() { }

Verwenden Sie eine Datei, um das Modul zu definieren:

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

Eine Datei verwendet nur das Modul, um eine Komponente zu definieren

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

function SomeController() { }

und eine andere Datei, um eine andere Komponente zu definieren

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

function someFactory() { }

Natürlich gibt es viele andere Regeln für Module, Controller und Dienste, die sehr nützlich und lesenswert sind.

Und dank des Kommentars von ya_dimon sollte der obige Code in IIFE eingeschlossen werden, zum Beispiel:

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

Gute Antwort und toller Link.
Ellesedil

Wenn ich verschiedene Controller in verschiedenen Javascript-Dateien habe, muss dann nicht mehr Dateien geladen werden, dh mehr Server-Treffer?
Vignesh Subramanian

Es ist ziemlich einfach, sie mit schlucken oder grunzen, vignesh zusammenzuführen / zu uglifizieren / umzubenennen, und ich persönlich liebe schlucken.
Aqingsao

1
Sie haben vergessen hinzuzufügen, dass all diese Snippets in IIFE sein sollten, sonst haben Sie Funktionen wie "someFactory ()" global. Es besteht die Möglichkeit von Namenskollisionen. (und Sie brauchen kein IIFE in es6)
ya_dimon

12

Ich hatte vor kurzem auch dieses Rätsel. Ich hatte genau wie Sie mit der verketteten Syntax angefangen, aber auf lange Sicht wird es bei großen Projekten unhandlich. Normalerweise würde ich ein Controller-Modul, ein Servicemodul usw. in separaten Dateien erstellen und sie in mein Hauptanwendungsmodul einfügen, das sich in einer anderen Datei befindet. Beispielsweise:

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

Aber jede dieser Dateien wurde mit dem Wachstum des Projekts viel zu groß. Deshalb habe ich beschlossen, sie je nach Controller oder Dienst in separate Dateien aufzuteilen. Ich fand, dass die Verwendung angular.module('mod-name').ohne das Injektionsarray das ist, was Sie brauchen, damit dies funktioniert. Das Deklarieren einer globalen Variablen in einer Datei und das Erwarten, dass diese in einer anderen Datei verfügbar ist, funktioniert einfach nicht oder kann zu unerwarteten Ergebnissen führen.

Kurz gesagt, meine Bewerbung sah ungefähr so ​​aus:

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

Ich habe dies auch für die Servicedatei getan. Es ist nicht erforderlich, die Hauptanwendungsmoduldatei zu ändern, in die Sie immer noch dieselben Module einfügen würden.


1
Was bringt es, separate Module für Dienste / Anweisungen / Controller zu erstellen?
Filip Sobczak

2
In großen Projekten kann es schwierig werden, Dinge zu finden, wenn Controller / Filter / Direktiven / Dienste miteinander verschachtelt sind. Dies ist nur eine Möglichkeit, die Dinge organisiert zu halten.
Mekonroy

1
@FilipSobczak Er erstellt KEINE separaten Module für Dienste / Anweisungen / Controller. Vielmehr hat er das Modul nur einmal mit erstellt angular.module('my-controllers',[]);(Beachten Sie, dass er das [] nur einmal für die Deklaration angibt). Er verwendet dies einfach in den anderen Dateien wieder. Die Trennung von Dateien macht es relativ einfach, das Projekt zu pflegen, insbesondere große.
Devner

8

Eine andere Praxis besteht darin, Controller, Direktiven usw. in ihre eigenen Module zu packen und diese Module in Ihr "Haupt" -Modul einzufügen:

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

Im globalen Bereich bleibt nichts übrig.

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


Warum verwenden Sie app.controllersinsted controllersals Modulnamen. Gibt es einen Vorteil? Ich bin ein Neuling in Angularjs
sijo vijayan

4

Ich teile gerne meine Dateien und meine Module.

Etwas wie das:

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

Ich bin kein großer Fan des "verketteten Stils", deshalb schreibe ich meine Variable immer lieber auf.


2
So habe ich es gemacht, aber jede Datei services.js oder controller.js wird in einem Großprojekt schnell groß. Schließlich müssen Sie jeden Dienst oder Controller in eine separate Datei aufteilen.
Mekonroy

1
@meconroy Genau. Wenn das Ding immer größer wird, teile ich die Direktive gerne in kleinere Module auf und füge sie dann in das "Haupt" -Richtlinienmodul ein.
Beterraba


0

Verkettung ist für mich der kompakteste Weg:

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

Auf diese Weise kann ich problemlos Komponenten zwischen Modulen verschieben, muss nie dasselbe Modul zweimal deklarieren und benötige niemals globale Variablen.

Und wenn die Datei zu lang wird, ist die Lösung einfach - aufgeteilt in zwei Dateien, von denen jede oben ihr eigenes Modul deklariert. Für mehr Transparenz versuche ich, ein eindeutiges Modul pro Datei beizubehalten und es so zu benennen, dass es dem vollständigen Pfad der Datei ähnelt. Auf diese Weise muss ich auch nie ein Modul ohne schreiben [], was ein häufiger Schmerzpunkt ist.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.