AngularJSのモジュール宣言のベストプラクティス?


115

アプリで多数のAngularモジュールが宣言されています。私は当初、次のような「連鎖」構文を使用してそれらを宣言し始めました。

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

しかし、それを読むのは簡単ではないと判断したので、次のようなモジュール変数を使用して宣言し始めました。

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

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

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

2番目の構文は私にはずっと読みやすいように見えますが、私の唯一の不満は、この構文はmod変数をグローバルスコープに残さないことです。という名前の変数がある場合mod、次の変数(およびグローバル変数に関連する他の問題)で上書きされます。

だから私の質問は、これが最善の方法ですか?または、このようなことをする方が良いでしょうか?:

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

それとも気にするのに十分重要ですか?モジュール宣言の「ベストプラクティス」が何であるか知りたいだけです。前もって感謝します。


回答:


118

モジュールを宣言する「最善の」方法

angularはグローバルスコープ自体にあり、モジュールは変数に保存されるので、次の方法でモジュールにアクセスできます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){
      //..
  }])
})();

他のグローバル変数は必要ありません。

もちろん、それはすべての好みに依存しますが、これは一種の最良の実践だと思います。

  1. グローバルスコープを汚染する必要はありません
  2. どこからでもモジュールにアクセスして、モジュールとその機能を自由に別のファイルに分類できます。
  3. 「厳密な使用」の関数形式を使用できます。
  4. ファイルの読み込み順序はそれほど重要ではありません

モジュールとファイルを並べ替えるためのオプション

モジュールを宣言してアクセスするこの方法により、非常に柔軟になります。関数タイプ(別の回答で説明されているような)またはルートを介してモジュールをソートできます。例:

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

最終的にどのように分類するかは、個人の好みとプロジェクトの規模とタイプの問題です。私は個人的には、モジュールの再利用性を高めるために、すべての異なるテストファイルを含め、同じフォルダー内のモジュールのすべてのファイル(ディレクティブ、コントローラー、サービス、フィルターのサブフォルダーに順序付け)をグループ化するのが好きです。したがって、中規模プロジェクトでは、すべての基本ルートとそのコントローラー、サービス、ディレクティブ、および多かれ少なかれ複雑なサブモジュールを含むベースモジュールができます。 :

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

非常に大きなプロジェクトの場合、上記のようにルートによって、またはいくつかの選択されたメインルートによって、またはルートといくつかの選択されたコンポーネントの組み合わせによっても、モジュールをグループ化することがありますが、それは本当に依存します。

編集: それが関連していて、私が最近非常に最近遭遇したからです:(angular.module-functionに2番目のパラメーターを追加することによって)モジュールを1回だけ作成するように十分注意してください。これはアプリケーションを混乱させ、検出が非常に困難になる可能性があります。

並べ替えモジュールに関する2015年の編集: 1年半の角度体験の後で、AMDはAngularやサービス、ディレクティブ、フィルターではまだうまく機能しないため、アプリ内で別の名前のモジュールを使用することの利点は多少制限されることを付け加えますとにかく、角度のコンテキスト内でグローバルに利用できます(ここで例を示します)。ただし、意味的および構造的なメリットはまだあります。コードを1行コメントアウトまたはコメントアウトして、モジュールを含めたり除外したりできると便利です。

また、通常は互いに依存しているため、サブモジュールをタイプ(例: 'myMapSubModule.controllers')で分離することはほとんど意味がありません


7
あなたは生命維持(すぐに呼び出される関数式)、別名匿名の自己実行機能は必要ありません
plus-

1
あなたが正しい。関数形式の「use strict」を適用する場合にのみ必要です。しかし、害はありません。
hugo der hungrige 2013年

1
ほとんどの場合'use strict';、コンポーネント内に配置することもできます。module.controller(function () { 'use strict'; ... });
ジャクソン

私は実装が好きですが、私はチェーンをするのも楽しいわけではないので、これをベテラバのやっていることと混ぜています
Mirko

1
AMDを使用する場合は、appという名前の単一のモジュールで十分です。requireステートメントを削除することでAMDモジュールを除外できます。また、コントローラーやサービスを登録する必要はもうありません。
James

28

私はジョンパパ角度スタイルガイドが大好きで、この質問に関連するいくつかのルールがあります:

ルール:名前付き関数と匿名関数

匿名関数の使用は避けてください:

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

代わりに、名前付き関数を使用します。

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

function Dashboard() { }

著者が言うように: This produces more readable code, is much easier to debug, and reduces the amount of nested callback code.

ルール:ファイルごとに1つのコンポーネントを定義します。

1つのファイルで複数のコンポーネントを使用しない:

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

function SomeController() { }

function someFactory() { }

代わりに、1つのファイルを使用してモジュールを定義します。

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

1つのファイルはモジュールを使用してコンポーネントを定義するだけです

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

function SomeController() { }

別のコンポーネントを定義する別のファイル

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

function someFactory() { }

もちろん、モジュール、コントローラー、およびサービスには、他にも非常に便利で読む価値のある多くのルールがあります。

ya_dimonのコメントのおかげで、上記のコードはIIFEでラップする必要があります。次に例を示します。

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

良い答えと素晴らしいリンク。
Ellesedil 2015

異なるjavascriptファイルに異なるコントローラーがある場合、より多くのファイルのロード、つまりより多くのサーバーヒットを必要としないでしょうか?
Vignesh Subramanian 2015

それらをgulpやgrunt、vigneshでマージ/ uglify /名前変更するのは非常に簡単で、個人的にはgulpが大好きです。
aqingsao 2015

1
追加するのを忘れて、このスニペットはすべてIIFEにある必要があります。そうでない場合は、「someFactory()」のような関数がグローバルにあります。名前の衝突の可能性があります。(ES6ではIIFEは必要ありません)
ya_dimon 2016年

12

私も最近この問題を抱えていました。私はあなたが連鎖構文を使うのと同じように始めましたが、長い目で見れば大規模なプロジェクトでは扱いにくくなります。通常、コントローラーモジュールやサービスモジュールなどを別々のファイルに作成し、別のファイルにあるメインアプリケーションモジュールに挿入します。例えば:

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

しかし、プロジェクトが成長するにつれ、これらの各ファイルは大きくなってきました。そこで、各コントローラーまたはサービスに基づいて、それらを個別のファイルに分割することにしました。angular.module('mod-name').インジェクションアレイなしで使用すると、これが機能するために必要なことがわかりました。1つのファイルでグローバル変数を宣言し、それを別のファイルですぐに利用できると期待しても、機能しないか、予期しない結果になる可能性があります。

つまり、私のアプリケーションは次のようになります。

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

これをservicesファイルにも行いました。同じモジュールを挿入するメインアプリケーションモジュールファイルを変更する必要はありません。


1
サービス/ディレクティブ/コントローラー用に個別のモジュールを作成する意味は何ですか?
Filip Sobczak 2014年

2
大規模なプロジェクトでは、コントローラー/フィルター/ディレクティブ/サービスがすべて一緒にインターリーブされると、物事を見つけるのが難しくなります。これは、物事を整理しておくための1つの方法にすぎません。
meconroy 2014年

1
@FilipSobczak彼は、サービス/ディレクティブ/コントローラー用の個別のモジュールを作成していません。むしろ、彼は一度だけ使用してモジュールを作成しましたangular.module('my-controllers',[]);(宣言のために[]を一度だけ指定していることに注意してください)。彼は単にこれを他のファイルで再利用しています。ファイルの分離により、プロジェクト、特に大きなプロジェクトの保守が比較的容易になります。
Devner

8

もう1つの方法は、コントローラー、ディレクティブなどを独自のモジュールに組み込み、それらのモジュールを「メイン」モジュールに挿入することです。

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

グローバルスコープには何も残っていません。

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


モジュール名としてapp.controllersinsted of を使用するのはなぜですかcontrollers、何か利点はありますか?私はAngularjsの新人です
Sijo Vijayan

4

ファイルとモジュールを分割するのが好きです。

このようなもの:

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

私は「連鎖スタイル」の大ファンではないので、変数を常に書き留めることを好みます。


2
これは私が行っていた方法ですが、services.jsまたはcontroller.jsの各ファイルは大規模なプロジェクトで高速になり、最終的には各サービスまたはコントローラーを個別のファイルに分割する必要があります。
meconroy 2013年

1
@meconroyその通りです。物事がどんどん大きくなったら、ディレクティブを小さなモジュールに分割して、「メイン」ディレクティブモジュールに注入したいと思います。
Beterraba 2013年


0

私にとって、チェーンは最もコンパクトな方法です。

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

そうすれば、モジュール間でコンポーネントを簡単に移動でき、同じモジュールを2回宣言する必要がなく、グローバル変数も必要ありません。

そして、ファイルが長くなりすぎた場合の解決策は簡単です。2つのファイルに分割し、それぞれが上部に独自のモジュールを宣言しています。透明性を高めるために、ファイルごとに1つの一意のモジュールを保持し、ファイルの完全パスに似た名前を付けます。この方法でも、なし[]でモジュールを記述する必要はありません。これは一般的な問題です。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.