膨大な量の貴重な情報源のおかげで、AngularJSアプリにコンポーネントを実装するための一般的な推奨事項がいくつかあります。
コントローラ
コントローラは、モデルとビューの間の単なる中間層である必要があります。できるだけ薄くするようにしてください。
コントローラのビジネスロジックを回避することを強くお勧めします。モデルに移動する必要があります。
コントローラーは、メソッド呼び出し(子供が親と通信したい場合に可能)または$ emit、$ broadcast、および$ onメソッドを使用して他のコントローラーと通信できます。発信およびブロードキャストされるメッセージは最小限に抑える必要があります。
コントローラは、プレゼンテーションやDOM操作を気にする必要はありません。
ネストされたコントローラは避けてください。この場合、親コントローラーはモデルとして解釈されます。代わりに、モデルを共有サービスとして挿入します。
コントローラーのスコープは、プレゼンテーションモデルのデザインパターンと同様に、ビューとモデルをバインドし、ビューモデルを
カプセル化するために使用する必要があります。
範囲
テンプレートではスコープを読み取り専用として、コントローラーでは書き込み専用として扱います。スコープの目的は、モデルではなくモデルを参照することです。
双方向バインディング(ng-model)を行う場合は、スコーププロパティに直接バインドしないようにしてください。
型番
AngularJSのモデルは、サービスによって定義されるシングルトンです。
モデルは、データを分離して表示する優れた方法を提供します。
モデルは通常、1つの依存関係(ある種のイベントエミッター、一般的には$ rootScope)を持ち、テストしやすいドメインロジックを含んでいるため、ユニットテストの主要な候補です。
モデルは、特定のユニットの実装と見なす必要があります。それは単一の責任の原則に基づいています。ユニットは、関連するロジックの独自のスコープを担当するインスタンスであり、現実の世界で単一のエンティティを表し、データと状態の観点からプログラミングの世界でそれを記述します。
モデルはアプリケーションのデータをカプセル化し、
そのデータにアクセスして操作するためのAPIを提供する必要があります。
モデルは、同様のアプリケーションに簡単に転送できるようにポータブルでなければなりません。
モデル内のユニットロジックを分離することで、検索、更新、維持がより簡単になりました。
モデルは、アプリケーション全体に共通する、より一般的なグローバルモデルのメソッドを使用できます。
コンポーネントの結合を減らし、ユニットのテスト容易性と使いやすさを高めることが実際に依存しない場合は、依存関係注入を使用して他のモデルをモデルに構成しないようにしてください。
モデルでイベントリスナーを使用しないようにします。それはそれらをテストすることを困難にし、一般に単一の責任原理の観点からモデルを殺します。
モデルの実装
モデルはデータと状態の観点からいくつかのロジックをカプセル化する必要があるため、メンバーへのアクセスを構造的に制限する必要があります。これにより、疎結合を保証できます。
AngularJSアプリケーションでそれを行う方法は、ファクトリサービスタイプを使用して定義することです。これにより、プライベートプロパティとメソッドを非常に簡単に定義でき、パブリックにアクセス可能なものを1か所で返すことができるため、開発者にとって読みやすくなります。
例:
angular.module('search')
.factory( 'searchModel', ['searchResource', function (searchResource) {
var itemsPerPage = 10,
currentPage = 1,
totalPages = 0,
allLoaded = false,
searchQuery;
function init(params) {
itemsPerPage = params.itemsPerPage || itemsPerPage;
searchQuery = params.substring || searchQuery;
}
function findItems(page, queryParams) {
searchQuery = queryParams.substring || searchQuery;
return searchResource.fetch(searchQuery, page, itemsPerPage).then( function (results) {
totalPages = results.totalPages;
currentPage = results.currentPage;
allLoaded = totalPages <= currentPage;
return results.list
});
}
function findNext() {
return findItems(currentPage + 1);
}
function isAllLoaded() {
return allLoaded;
}
// return public model API
return {
/**
* @param {Object} params
*/
init: init,
/**
* @param {Number} page
* @param {Object} queryParams
* @return {Object} promise
*/
find: findItems,
/**
* @return {Boolean}
*/
allLoaded: isAllLoaded,
/**
* @return {Object} promise
*/
findNext: findNext
};
});
新しいインスタンスを作成する
これが依存関係の注入を分解し始め、ライブラリが特にサードパーティにとって不自然な動作をするようになるため、新しい機能を返すファクトリを使用しないようにしてください。
同じことを行うためのより良い方法は、ファクトリをAPIとして使用して、getterメソッドとsetterメソッドがアタッチされたオブジェクトのコレクションを返すことです。
angular.module('car')
.factory( 'carModel', ['carResource', function (carResource) {
function Car(data) {
angular.extend(this, data);
}
Car.prototype = {
save: function () {
// TODO: strip irrelevant fields
var carData = //...
return carResource.save(carData);
}
};
function getCarById ( id ) {
return carResource.getById(id).then(function (data) {
return new Car(data);
});
}
// the public API
return {
// ...
findById: getCarById
// ...
};
});
グローバルモデル
一般に、このような状況を回避し、モデルを適切に設計して、コントローラーに注入してビューで使用できるようにします。
特に、一部のメソッドでは、アプリケーション内でグローバルなアクセスが必要です。これを可能にするには、$ rootScopeで' common 'プロパティを定義し、アプリケーションのブートストラップ中にcommonModelにバインドします。
angular.module('app', ['app.common'])
.config(...)
.run(['$rootScope', 'commonModel', function ($rootScope, commonModel) {
$rootScope.common = 'commonModel';
}]);
すべてのグローバルメソッドは、 ' common 'プロパティ内に存在します。これは一種の名前空間です。
ただし、$ rootScopeで直接メソッドを定義しないでください。これは、ビュースコープ内でngModelディレクティブと一緒に使用すると予期しない動作を引き起こす可能性があり、通常はスコープを散らかし、スコープメソッドが問題をオーバーライドすることになります。
資源
リソースを使用すると、さまざまなデータソースを操作できます。
single-responsibility-principleを使用して実装する必要があります。
特に、HTTP / JSONエンドポイントへの再利用可能なプロキシです。
リソースはモデルに注入され、データを送信/取得する可能性を提供します。
リソースの実装
RESTfulサーバー側データソースとの対話を可能にするリソースオブジェクトを作成するファクトリ。
返されたリソースオブジェクトには、低レベルの$ httpサービスとやり取りすることなく高レベルの動作を提供するアクションメソッドがあります。
サービス
モデルもリソースもサービスです。
サービスは、関連付けられておらず、緩やかに結合された、自己完結型の機能単位です。
サービスは、Angularがサーバー側からクライアント側のWebアプリに提供する機能であり、サービスは長い間一般的に使用されてきました。
Angularアプリのサービスは、依存関係の注入を使用して相互に接続された置換可能なオブジェクトです。
Angularにはさまざまなタイプのサービスが付属しています。それぞれに独自の使用例があります。詳細については、サービスタイプについてをご覧ください。
アプリケーションのサービスアーキテクチャの主な原則を検討してください。
一般に、Webサービス用語集によると:
サービスは、プロバイダーエンティティとリクエスターエンティティの観点から一貫した機能を形成するタスクを実行する機能を表す抽象的なリソースです。サービスを使用するには、具体的なプロバイダーエージェントによってサービスを実現する必要があります。
クライアント側の構造
一般に、アプリケーションのクライアント側はモジュールに分割されます。各モジュールは、ユニットとしてテスト可能である必要があります。
タイプではなく、機能/機能またはビューに応じてモジュールを定義してください。詳細については、Miskoのプレゼンテーションを参照してください。
モジュールのコンポーネントは、従来、コントローラー、モデル、ビュー、フィルター、ディレクティブなどのタイプごとにグループ化できます。
ただし、モジュール自体は、再利用、転送、およびテストが可能なままです。
また、開発者がコードの一部とそのすべての依存関係を見つけるのもはるかに簡単です。
詳細については、大規模なAngularJSおよびJavaScriptアプリケーションでのコード編成を参照してください。
フォルダ構造の例:
|-- src/
| |-- app/
| | |-- app.js
| | |-- home/
| | | |-- home.js
| | | |-- homeCtrl.js
| | | |-- home.spec.js
| | | |-- home.tpl.html
| | | |-- home.less
| | |-- user/
| | | |-- user.js
| | | |-- userCtrl.js
| | | |-- userModel.js
| | | |-- userResource.js
| | | |-- user.spec.js
| | | |-- user.tpl.html
| | | |-- user.less
| | | |-- create/
| | | | |-- create.js
| | | | |-- createCtrl.js
| | | | |-- create.tpl.html
| |-- common/
| | |-- authentication/
| | | |-- authentication.js
| | | |-- authenticationModel.js
| | | |-- authenticationService.js
| |-- assets/
| | |-- images/
| | | |-- logo.png
| | | |-- user/
| | | | |-- user-icon.png
| | | | |-- user-default-avatar.png
| |-- index.html
角度のアプリケーション構築のよい例がで実装された角度アプリ - https://github.com/angular-app/angular-app/tree/master/client/src
これは、最新のアプリケーションジェネレーターでも考慮されます-https://github.com/yeoman/generator-angular/issues/109