Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

AngularJS styleguide for teams

NotificationsYou must be signed in to change notification settings

tama3bb/angularjs-styleguide

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

19 Commits
 
 

Repository files navigation

チーム開発のための AngularJS スタイルガイド by@toddmotto

チームで AngularJS アプリケーションを開発するための標準的なスタイルガイドとして、Angular アプリケーションについてのこれまでの記事講演、そして構築してきた経験を基にして、コンセプト、シンタックス、規約についてまとめている。

コミュニティ

John Papa と私は Angular スタイルのパターンについて話し合い、それによってこのガイドはよりすばらしいものとなっている。それぞれのスタイルガイドをリリースしているので、考えを比較するためにもJohn のスタイルガイドもぜひ確認してみてほしい。

See theoriginal article that sparked this off

Table of Contents

  1. Modules
  2. Controllers
  3. Services and Factory
  4. Directives
  5. Filters
  6. Routing resolves
  7. Publish and subscribe events
  8. Performance
  9. Angular wrapper references
  10. Comment standards
  11. Minification and annotation

Modules

  • 定義: 変数を使わずに setter / getter で module を定義

    // avoidvarapp=angular.module('app',[]);app.controller();app.factory();// recommendedangular.module('app',[]).controller().factory();
  • Note:angular.module('app', []); を setter、angular.module('app'); を getter として使う。setter で module を定義し、他のインスタンスからは getter でその module を取得して利用する。

  • メソッド: コールバックとして記述せず、function を定義してメソッドに渡す

    // avoidangular.module('app',[]).controller('MainCtrl',functionMainCtrl(){}).service('SomeService',functionSomeService(){});// recommendedfunctionMainCtrl(){}functionSomeService(){}angular.module('app',[]).controller('MainCtrl',MainCtrl).service('SomeService',SomeService);
  • コードのネストが深くなることを抑え、可読性を高められる

  • IIFE(イッフィー:即時関数式)スコープ: Angular に渡す function の定義でグローバルスコープを汚染することを避けるため、複数ファイルを連結(concatenate)するビルドタスクで IIFE 内にラップする

    (function(){angular.module('app',[]);// MainCtrl.jsfunctionMainCtrl(){}angular.module('app').controller('MainCtrl',MainCtrl);// SomeService.jsfunctionSomeService(){}angular.module('app').service('SomeService',SomeService);// ...})();

Back to top

Controllers

  • controllerAs: Controller はクラスであるため、常にcontrollerAs を利用する

    <!-- avoid --><divng-controller="MainCtrl">  {{ someObject }}</div><!-- recommended --><divng-controller="MainCtrl as main">  {{ main.someObject }}</div>
  • DOM で controller ごとに変数を定義し、$parent の利用を避ける

  • controllerAs では controller 内で$scope にバインドされるthis を利用する

    // avoidfunctionMainCtrl($scope){$scope.someObject={};$scope.doSomething=function(){};}// recommendedfunctionMainCtrl(){this.someObject={};this.doSomething=function(){};}
  • $emit$broadcast$on$watch で必要とならない限り、controllerAs では$scope を利用しない

  • 継承: controller クラスを拡張する場合は prototype 継承を利用する

    functionBaseCtrl(){this.doSomething=function(){};}BaseCtrl.prototype.someObject={};BaseCtrl.prototype.sharedSomething=function(){};AnotherCtrl.prototype=Object.create(BaseCtrl.prototype);functionAnotherCtrl(){this.anotherSomething=function(){};}
  • Object.create をレガシーブラウザでもサポートするためには polyfill を利用する

  • controllerAs 'vm': controller のthis コンテキストを、ViewModel を意味するvm として保持する

    // avoidfunctionMainCtrl(){this.doSomething=function(){};}// recommendedfunctionMainCtrl(SomeService){varvm=this;vm.doSomething=SomeService.doSomething;}

    Why? : Function コンテキストがthis の値を変えてしまうことによる.bind() の利用とスコープの問題を回避するため

  • プレゼンテーションロジックのみ (MVVM): controller 内ではプレゼンテーションロジックのみとし、ビジネスロジックは service に委譲する

    // avoidfunctionMainCtrl(){varvm=this;$http.get('/users').success(function(response){vm.users=response;});vm.removeUser=function(user,index){$http.delete('/user/'+user.id).then(function(response){vm.users.splice(index,1);});};}// recommendedfunctionMainCtrl(UserService){varvm=this;UserService.getUsers().then(function(response){vm.users=response;});vm.removeUser=function(user,index){UserService.removeUser(user).then(function(response){vm.users.splice(index,1);});};}

    Why? : controller では service からモデルのデータを取得するようにしてビジネスロジックを避け、ViewModel としてモデル・ビュー間のデータフローを制御させる。controller 内のビジネスロジックは service のテストを不可能にしてしまう。

Back to top

Services and Factory

  • すべての Angular Services はシングルトンで、.service().factory() はオブジェクトの生成され方が異なる

Services:constructor function としてnew で生成し、パブリックなメソッドと変数にthis を使う

```javascriptfunction SomeService () {  this.someMethod = function () {  };}angular  .module('app')  .service('SomeService', SomeService);```

Factory: ビジネスロジックやプロバイダモジュールで、オブジェクトやクロージャを返す

  • 常にホストオブジェクトを返す

    functionAnotherService(){varAnotherService={};AnotherService.someValue='';AnotherService.someMethod=function(){};returnAnotherService;}angular.module('app').factory('AnotherService',AnotherService);

    Why? : "Revealing Module Pattern" では primitive な値は更新されない

Back to top

Directives

  • restrict: 独自 directive にはcustom elementcustom attribute のみ利用する({ restrict: 'EA' }

    <!-- avoid --><!-- directive: my-directive --><divclass="my-directive"></div><!-- recommended --><my-directive></my-directive><divmy-directive></div>
  • コメントとクラス名での宣言は混乱しやすいため使うべきでない。コメントでの宣言は古いバージョンの IE では動作せず、属性での宣言が古いブラウザをカバーするのにもっとも安全である。

  • template: テンプレートをすっきりさせるためにArray.join('') を利用する

    // avoidfunctionsomeDirective(){return{template:'<div>'+'<h1>My directive</h1>'+'</div>'};}// recommendedfunctionsomeDirective(){return{template:['<div>','<h1>My directive</h1>','</div>'].join('')};}

    Why? : 適切なインデントでコードの可読性を高められ、不適切に+ を使ってしまうことによるエラーを避けられる

  • DOM 操作: directive 内のみとし、controller / service では DOM を操作しない

    // avoidfunctionUploadCtrl(){$('.dragzone').on('dragend',function(){// handle drop functionality});}angular.module('app').controller('UploadCtrl',UploadCtrl);// recommendedfunctiondragUpload(){return{restrict:'EA',link:function($scope,$element,$attrs){$element.on('dragend',function(){// handle drop functionality});}};}angular.module('app').directive('dragUpload',dragUpload);
  • 命名規約: 将来的に標準 directive と名前が衝突する可能性があるため、ng-* を独自 directive に使わない

    // avoid// <div ng-upload></div>functionngUpload(){return{};}angular.module('app').directive('ngUpload',ngUpload);// recommended// <div drag-upload></div>functiondragUpload(){return{};}angular.module('app').directive('dragUpload',dragUpload);
  • directive と filter は先頭文字を小文字で命名する。これは、Angular がcamelCase をハイフンつなぎとする命名規約によるもので、dragUpload が要素で使われた場合は<div drag-upload></div> となる。

  • controllerAs: directive でもcontrollerAs を使う

    // avoidfunctiondragUpload(){return{controller:function($scope){}};}angular.module('app').directive('dragUpload',dragUpload);// recommendedfunctiondragUpload(){return{controllerAs:'dragUpload',controller:function(){}};}angular.module('app').directive('dragUpload',dragUpload);

Back to top

Filters

  • グローバル filter: angular.filter() を使ってグローバルな filter を作成し、controller / service 内でローカルな filter を使わない

    // avoidfunctionSomeCtrl(){this.startsWithLetterA=function(items){returnitems.filter(function(item){return/^a/i.test(item.name);});};}angular.module('app').controller('SomeCtrl',SomeCtrl);// recommendedfunctionstartsWithLetterA(){returnfunction(items){returnitems.filter(function(item){return/^a/i.test(item.name);});};}angular.module('app').filter('startsWithLetterA',startsWithLetterA);
  • テストのしやすさと再利用性を高めるため

Back to top

Routing resolves

  • Promises:$routeProvider(またはui-router$stateProvider)内で controller の依存を解決する

    // avoidfunctionMainCtrl(SomeService){var_this=this;// unresolved_this.something;// resolved asynchronouslySomeService.doSomething().then(function(response){_this.something=response;});}angular.module('app').controller('MainCtrl',MainCtrl);// recommendedfunctionconfig($routeProvider){$routeProvider.when('/',{templateUrl:'views/main.html',resolve:{// resolve here}});}angular.module('app').config(config);
  • Controller.resolve プロパティ: ロジックを router にバインドせず、controller のresolve プロパティでロジックを関連付ける

    // avoidfunctionMainCtrl(SomeService){this.something=SomeService.something;}functionconfig($routeProvider){$routeProvider.when('/',{templateUrl:'views/main.html',controllerAs:'main',controller:'MainCtrl'resolve:{doSomething:function(){returnSomeService.doSomething();}}});}// recommendedfunctionMainCtrl(SomeService){this.something=SomeService.something;}MainCtrl.resolve={doSomething:function(SomeService){returnSomeService.doSomething();}};functionconfig($routeProvider){$routeProvider.when('/',{templateUrl:'views/main.html',controllerAs:'main',controller:'MainCtrl'resolve:MainCtrl.resolve});}
  • こうすることで controller と同じファイル内に resolve の依存を持たせ、router をロジックから開放する

Back to top

Publish and subscribe events

  • $scope: scope 間をつなぐイベントトリガーとして$emit$broadcast を使う

    // up the $scope$scope.$emit('customEvent',data);// down the $scope$scope.$broadcast('customEvent',data);
  • $rootScope: アプリケーション全体のイベントとして$emit を使い、忘れずにリスナーをアンバインドする

    // all $rootScope.$on listeners$rootScope.$emit('customEvent',data);
  • ヒント:$rootScope.$on リスナーは、$scope.$on リスナーと異なり常に残存するため、関連する$scope$destroy イベントを発生させたときに破棄する必要がある

    // call the closurevarunbind=$rootScope.$on('customEvent'[,callback]);$scope.$on('$destroy',unbind);
  • $rootScope リスナーが複数ある場合は、Object リテラルでループして$destroy イベント時に自動的にアンバインドさせる

    varrootListeners={'customEvent1':$rootScope.$on('customEvent1'[,callback]),'customEvent2':$rootScope.$on('customEvent2'[,callback]),'customEvent3':$rootScope.$on('customEvent3'[,callback])};for(varunbindinrootListeners){$scope.$on('$destroy',rootListeners[unbind]);}

Back to top

Performance

  • ワンタイムバインド: Angular の新しいバージョン(v1.3.0-beta.10+)では、ワンタイムバインドのシンタックス{{ ::value }} を利用する

    // avoid<h1>{{ vm.title }}</h1>// recommended<h1>{{ ::vm.title }}</h1>

    Why? :undefined の変数が解決されたときに$$watchers から取り除き、ダーティチェックでのパフォーマンスを改善する

  • $scope.$digest を検討:$scope.$apply でなく$scope.$digest を使い、子スコープのみを更新する

    $scope.$digest();

    Why? :$scope.$apply$rootScope.$digest を呼び出すため、アプリケーション全体の$$watchers をダーティチェックするが、$scope.$digest$scope のスコープと子スコープを更新する

Back to top

Angular wrapper references

  • $document と $window:$document$window を常に利用する

    // avoidfunctiondragUpload(){return{link:function($scope,$element,$attrs){document.addEventListener('click',function(){});}};}// recommendedfunctiondragUpload(){return{link:function($scope,$element,$attrs,$document){$document.addEventListener('click',function(){});}};}
  • $timeout と $interval: Angular の双方向データバインドが最新の状態を維持するよう$timeout$interval を利用する

    // avoidfunctiondragUpload(){return{link:function($scope,$element,$attrs){setTimeout(function(){//},1000);}};}// recommendedfunctiondragUpload($timeout){return{link:function($scope,$element,$attrs){$timeout(function(){//},1000);}};}

Back to top

Comment standards

  • jsDoc: jsDoc で function 名、説明、パラメータ、返り値をドキュメント化する

    /** *@name SomeService *@desc Main application Controller */functionSomeService(SomeService){/**   *@name doSomething   *@desc Does something awesome   *@param {Number} x First number to do something with   *@param {Number} y Second number to do something with   *@returns {Number}   */this.doSomething=function(x,y){returnx*y;};}angular.module('app').service('SomeService',SomeService);

Back to top

Minification and annotation

  • ng-annotate:ng-min は deprecated なので、ng-annotate for Gulp を利用し、/** @ngInject */ で function にコメントして自動的に DI (dependency injection) させる

    /** *@ngInject */functionMainCtrl(SomeService){this.doSomething=SomeService.doSomething;}angular.module('app').controller('MainCtrl',MainCtrl);
  • 以下のような$inject アノテーションを含む出力となる

    /** *@ngInject */functionMainCtrl(SomeService){this.doSomething=SomeService.doSomething;}MainCtrl.$inject=['SomeService'];angular.module('app').controller('MainCtrl',MainCtrl);

Back to top

Angular docs

その他、API リファレンスなどの情報は、Angular documentation を確認する。

Contributing

Open an issue first to discuss potential changes/additions.

License

(The MIT License)

Copyright (c) 2014 Todd Motto

Permission is hereby granted, free of charge, to any person obtaininga copy of this software and associated documentation files (the'Software'), to deal in the Software without restriction, includingwithout limitation the rights to use, copy, modify, merge, publish,distribute, sublicense, and/or sell copies of the Software, and topermit persons to whom the Software is furnished to do so, subject tothe following conditions:

The above copyright notice and this permission notice shall beincluded in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OFMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANYCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THESOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

About

AngularJS styleguide for teams

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

[8]ページ先頭

©2009-2025 Movatter.jp