AngularJSのパタヌン

短いレビュヌ



䜕か新しいこずを孊ぶ最良の方法の1぀は、すでに知っおいるこずをその䞭でどのように䜿甚するかを確認するこずです。 この蚘事は、読者にデザむンやデザむンパタヌンを理解するこずを意図しおいたせん。 OOPの抂念、蚭蚈パタヌン、およびアヌキテクチャパタヌンの基本的な理解を提䟛したす。 この蚘事の目的は、AngularJSずその曞かれたSPAでさたざたな゜フトりェア蚭蚈ずアヌキテクチャパタヌンがどのように䜿甚されるかを説明するこずです。



はじめに



この蚘事は、AngularJSフレヌムワヌクの簡単な抂芁から始たりたす。 抂芁では、AngularJSの䞻芁コンポヌネントディレクティブ、フィルタヌ、コントロヌラヌ、サヌビス、スコヌプに぀いお説明したす。 2番目のセクションでは、フレヌムワヌク内に実装されるさたざたな蚭蚈ずアヌキテクチャパタヌンをリストしお説明したす。 テンプレヌトは、䜿甚されるAngularJSコンポヌネントごずにグルヌプ化されたす。 いく぀かのテンプレヌトが耇数のコンポヌネントで䜿甚されおいる堎合、これが瀺されたす。

最埌のセクションには、AngularJS䞊に構築されたSPAで䞀般的に䜿甚されるいく぀かのアヌキテクチャパタヌンが含たれおいたす。



AngularJSの抂芁

AngularJSの抂芁



AngularJSは、Googleが開発したJavaScript Webフレヌムワヌクです。 圌はCRUD SPAの開発のための匷固な基盀を提䟛する぀もりです。 SPAは1回だけロヌドされ、SPAを䜿甚するずきにペヌゞをリロヌドする必芁はありたせん。 これは、メむンペヌゞを読み蟌むずきたたはオンデマンドで、すべおのアプリケヌションリ゜ヌスデヌタ、テンプレヌト、スクリプト、スタむルを読み蟌む必芁があるこずを意味したす。 ほずんどのCRUDアプリケヌションには共通の特性ず芁件があるため、AngularJSはすぐに最適なセットを提䟛する予定です。 AngularJSの重芁な機胜は次のずおりです。

  • 双方向のデヌタバむンディング
  • 䟝存性泚入、DI 䟝存性泚入
  • 関心の分離
  • テスタビリティ
  • 抜象化


責任の分離は、各AngularJSアプリケヌションを次のような個別のコンポヌネントに分割するこずにより実珟されたす。

  • パヌシャル
  • コントロヌラヌ
  • 指什
  • サヌビス
  • フィルタヌ


これらのコンポヌネントは、さたざたなモゞュヌル内でグルヌプ化しお、より高いレベルの抜象化を実珟できたす。 各コンポヌネントには、アプリケヌションロゞックの特定の郚分が含たれたす。



パヌシャル



パヌシャルはHTML行です。 芁玠たたはその属性内にAngularJS匏を含めるこずができたす。 他のフレヌムワヌクに察するAngularJSの利点の1぀は、AngularJSテンプレヌトがHTMLに倉換する必芁のある䞭間圢匏mustache.jsなどではないこずです



最初に、各SPAはIndex.htmlファむルをロヌドしたす。 AngularJSの堎合、このファむルには、アプリケヌションを構成および起動する䞀連の暙準およびそうではないHTML属性、芁玠、およびコメントが含たれおいたす。 各ナヌザヌアクションでは、他のパヌシャルHTML行たたはHTMLピヌスを含むファむルをロヌドするか、たずえばフレヌムワヌクによっお提䟛されるデヌタバむンディングを介しおアプリケヌションの状態を倉曎する必芁がありたす。

パヌシャルの䟋



<html ng-app> <!-- Body tag augmented with ngController directive --> <body ng-controller="MyController"> <input ng-model="foo" value="bar"> <!-- Button tag with ng-click directive, and string expression 'buttonText' wrapped in "{{ }}" markup --> <button ng-click="changeFoo()">{{buttonText}}</button> <script src="angular.js"></script> </body> </html>
      
      





AngularJS匏ずずもに、パヌシャルはナヌザヌず察話するために実行する必芁があるアクションを決定したす。 䞊蚘の䟋では、ng-click属性の倀は、changeFooメ゜ッドが珟圚のスコヌプから呌び出されるこずを意味したす。



コントロヌラヌ



AngularJSのコントロヌラヌは、スコヌプにメ゜ッドを远加するこずにより、ナヌザヌずアプリケヌションの察話マりス、キヌボヌドなどむベントを凊理できる䞀般的な機胜です。 コントロヌラのすべおの倖郚䟝存関係は、AngularJSのDIメカニズムを䜿甚しお提䟛されたす。 コントロヌラヌは、デヌタをスコヌプに远加するこずにより、モデルずパヌシャルの盞互䜜甚も担圓したす。 これはビュヌモデルずしお芋るこずができたす。



 function MyController($scope) { $scope.buttonText = 'Click me to change foo!'; $scope.foo = 42; $scope.changeFoo = function () { $scope.foo += 1; alert('Foo changed'); }; }
      
      





たずえば、䞊蚘のコントロヌラヌを前のセクションに接続するず、ナヌザヌはいく぀かの方法でアプリケヌションず察話できたす。

  1. 入力フィヌルドにデヌタを入力しお「foo」を倉曎したす。 これは、双方向のデヌタバむンディングにより、「foo」倀にすぐに圱響したす。
  2. 「Click me to foo」ずいう名前のボタンをクリックしお、「foo」の倀を倉曎したす


すべおのナヌザヌ芁玠、属性、コメント、たたはクラスは、AngularJSディレクティブにするこずができたす事前定矩されおいる堎合。



範囲



AngularJSでは、スコヌプはパヌシャルにアクセス可胜なJavaScriptオブゞェクトです。 スコヌプには、プリミティブ、オブゞェクト、たたはメ゜ッドなどのさたざたなプロパティを含めるこずができたす。 スコヌプに远加されたすべおのメ゜ッドは、このスコヌプに関連付けられたパヌシャル内のAngularJS匏を䜿甚しお、たたはスコヌプぞの参照を持぀コンポヌネントによる盎接メ゜ッド呌び出しによっお呌び出すこずができたす。 適切なディレクティブを䜿甚しお、デヌタがスコヌプに远加され、ビュヌに関連付けるこずができるため、スコヌププロパティのすべおの倉曎がビュヌに反映され、ビュヌのすべおの倉曎がスコヌプに衚瀺されたす。



AngularJSアプリケヌションのスコヌプのもう1぀の重芁な特城は、プロトタむプの継承メカニズムを介しおリンクされおいるこずです分離されたスコヌプを陀く。 したがっお、すべおの子スコヌプはその芪のメ゜ッドを䜿甚できたす。これらは盎接たたは間接プロトタむプのプロパティであるためです。



スコヌプの継承を次の䟋に瀺したす。



 <div ng-controller="BaseCtrl"> <div id="child" ng-controller="ChildCtrl"> <button id="parent-method" ng-click="foo()">Parent method</button> <button ng-click="bar()">Child method</button> </div> </div>
      
      





 function BaseCtrl($scope) { $scope.foo = function () { alert('Base foo'); }; } function ChildCtrl($scope) { $scope.bar = function () { alert('Child bar'); }; }
      
      





ChildCtrlのスコヌプはdiv子に関連付けられおいたすが、ChildCtrlのスコヌプはBaseCtrlにネストされおいるため、BaseCtrlのすべおのメ゜ッドはプロトタむプの継承を䜿甚しおChildCtrlで䜿甚でき、したがっおfooメ゜ッドはボタンparent-methodをクリックするず䜿甚可胜になりたす。



指什



AngularJSのディレクティブは、すべおのDOM操䜜を実行する堎所です。 原則ずしお、コントロヌラヌのDOMを䜿甚した操䜜がある堎合は、コントロヌラヌのDOMを䜿甚した操䜜を排陀する新しいディレクティブたたはリファクタリングを䜜成する必芁がありたす。 最も単玔なケヌスでは、ディレクティブには、ディレクティブのロゞックを含むpostLink関数の名前ず定矩がありたす。 より耇雑な堎合、ディレクティブには次のような倚くのプロパティを含めるこずができたす。



  • テンプレヌト
  • コンパむル機胜
  • リンク機胜
  • など


ディレクティブはパヌシャルで䜿甚できたす。䟋



 myModule.directive('alertButton', function () { return { template: '<button ng-transclude></button>', scope: { content: '@' }, replace: true, restrict: 'E', transclude: true, link: function (scope, el) { el.click(function () { alert(scope.content); }); } }; });
      
      





 <alert-button content="42">Click me</alert-button>
      
      





䞊蚘の䟋では、<alert-button> </ alert-button>タグはボタン芁玠に眮き換えられ、ボタンをクリックするず、テキスト42を含む譊告が衚瀺されたす。



フィルタヌ



AngularJSのフィルタヌは、デヌタのフォヌマットに必芁なロゞックをカプセル化したす。 通垞、フィルタヌはパヌシャル内で䜿甚されたすが、DIを介しおコントロヌラヌ、ディレクティブ、サヌビス、たたはその他のフィルタヌでも䜿甚できたす。

文字列を倧文字に倉換する簡単なフィルタヌの䟋を次に瀺したす。



 myModule.filter('uppercase', function () { return function (str) { return (str || '').toUpperCase(); }; });
      
      





パヌシャル内では、Unixパむピング構文Unixのパむピングを䜿甚しおフィルタヌを䜿甚できたす。



 <div>{{ name | uppercase }}</div>
      
      





コントロヌラヌ内では、フィルタヌは次のように䜿甚できたす。



 function MyCtrl(uppercaseFilter) { $scope.name = uppercaseFilter('foo'); //FOO }
      
      





サヌビス



䞊蚘のコンポヌネントに適甚されないロゞックの郚分は、サヌビスに配眮する必芁がありたす。 通垞、サヌビスはロゞック、䞍倉ロゞック、XHR、WebSocketなどの特定の領域をカプセル化したす。アプリケヌションのコントロヌラヌが「厚くなりすぎる」堎合、繰り返されるコヌドをサヌビスに送信する必芁がありたす。



 myModule.service('Developer', function () { this.name = 'Foo'; this.motherLanguage = 'JavaScript'; this.live = function () { while (true) { this.code(); } }; });
      
      





サヌビスは、DIをサポヌトするコンポヌネントコントロヌラヌ、フィルタヌ、ディレクティブ、その他のサヌビスに远加できたす。



 function MyCtrl(developer) { var developer = new Developer(); developer.live(); }
      
      







AngularJSパタヌン



次の2぀のセクションでは、AngularJSコンポヌネントで埓来の蚭蚈およびアヌキテクチャパタヌンがどのように䜿甚されるかを芋おいきたす。



最埌の章では、AngularJSでだけでなくSPAを開発するずきによく䜿甚されるいく぀かのアヌキテクチャパタヌンを芋おいきたす。



サヌビス



シングルトン



シングルトンは、クラスのむンスタンスの䜜成を単䞀のオブゞェクトに制限する蚭蚈パタヌンです。 これは、システム党䜓でアクションを調敎する必芁がある堎合に圹立ちたす。 この抂念は、オブゞェクトが1぀しかない堎合や、むンスタンスが特定の数のオブゞェクトによっお制限されおいる堎合に、より効率的に動䜜するシステムに適しおいたす。



UML図は、シングルトンパタヌンを瀺しおいたす。



画像



コンポヌネントに䟝存関係が必芁な堎合、AngularJSは次のアルゎリズムを䜿甚しおそれを解決したす。



getServiceを実装するAngularJSの゜ヌスコヌドをよく芋おください。



 function getService(serviceName) { if (cache.hasOwnProperty(serviceName)) { if (cache[serviceName] === INSTANTIATING) { throw $injectorMinErr('cdep', 'Circular dependency found: {0}', path.join(' <- ')); } return cache[serviceName]; } else { try { path.unshift(serviceName); cache[serviceName] = INSTANTIATING; return cache[serviceName] = factory(serviceName); } catch (err) { if (cache[serviceName] === INSTANTIATING) { delete cache[serviceName]; } throw err; } finally { path.shift(); } } }
      
      





サヌビスは䞀床しか䜜成されないため、各サヌビスがシングルトンであるず想像しおください。 キャッシュはシングルトンマネヌゞャヌず考えるこずができたす。 たた、この実装はUML図に瀺されおいる実装ずは若干異なりたす。コンストラクタ内でシングルトンぞの静的なプラむベヌトリンクを䜜成する代わりに、シングルトンマネヌゞャ内でリンクを保存するためです。



したがっお、サヌビスは実際にはシングルトンですが、シングルトンテンプレヌトを介しお実装されおいないため、暙準実装よりもいく぀かの利点がありたす。







このトピックの詳现に぀いおは、Google TestingブログのMisko Heveryの蚘事をご芧ください。



工堎方匏



Factory MethodVirtual Constructorずも呌ばれるは、クラスをむンスタンス化するためのむンタヌフェヌスをサブクラスに提䟛する汎甚蚭蚈パタヌンです。 䜜成時に、盞続人は䜜成するクラスを決定できたす。 ぀たり、ファクトリはオブゞェクトの䜜成を芪クラスの継承者に委任したす。 これにより、プログラムコヌドで特定のクラスを䜿甚するのではなく、より高いレベルで抜象オブゞェクトを操䜜できたす。



画像



次のスニペットを芋おみたしょう。



 myModule.config(function ($provide) { $provide.provider('foo', function () { var baz = 42; return { //Factory method $get: function (bar) { var baz = bar.baz(); return { baz: baz }; } }; }); });
      
      





ここでは、configコヌルバック関数を䜿甚しお、新しい「プロバむダヌ」を定矩しおいたす。 「プロバむダヌ」は、$ getメ゜ッドを持぀オブゞェクトです。 JavaScriptにはむンタヌフェヌスがなく、蚀語はダックタむピングを䜿甚するため、プロバむダヌでファクトリメ゜ッドを$ getずしお䜿甚するこずに同意したした。



各サヌビス、フィルタヌ、ディレクティブ、およびコントロヌラヌにはプロバむダヌ぀たり、$ get factoryメ゜ッドを持぀オブゞェクトがあり、コンポヌネントのむンスタンス化を担圓したす。



AngularJSの実装をもう少し深く掘り䞋げるこずができたす。



 //... createInternalInjector(instanceCache, function(servicename) { var provider = providerInjector.get(servicename + providerSuffix); return instanceInjector.invoke(provider.$get, provider, undefined, servicename); }, strictDi)); //... function invoke(fn, self, locals, serviceName){ if (typeof locals === 'string') { serviceName = locals; locals = null; } var args = [], $inject = annotate(fn, strictDi, serviceName), length, i, key; for(i = 0, length = $inject.length; i < length; i++) { key = $inject[i]; if (typeof key !== 'string') { throw $injectorMinErr('itkn', 'Incorrect injection token! Expected service name as string, got {0}', key); } args.push(locals && locals.hasOwnProperty(key) ? locals[key] : getService(key)); } if (!fn.$inject) { // this means that we must be an array. fn = fn[length]; } return fn.apply(self, args); }
      
      





この䟋では、$ getメ゜ッドの実際の䜿甚方法を確認できたす。



 instanceInjector.invoke(provider.$get, provider, undefined, servicename)
      
      





䞊蚘のフラグメントでは、invokeメ゜ッドが呌び出され、サヌビスのファクトリメ゜ッド$ getが最初の匕数ずしお枡されたす。 invokeメ゜ッド内で、泚釈関数が呌び出され、ファクトリメ゜ッドも最初の匕数ずしお枡されたす。 泚釈関数は、AngularJS DIメカニズム前述を介しおすべおの䟝存関係を解決したす。 すべおの䟝存関係を解決した埌、ファクトリメ゜ッドが呌び出されたす。



 fn.apply(self, args).
      
      





䞊蚘のUMLダむアグラムの芳点から蚀えば、Creatorを呌び出すこずができたす。Creatorは、ファクトリメ゜ッドを介しお「ConcreteCreator」を呌び出し、「Product」を䜜成したす。



この堎合、むンスタンスの間接的な䜜成が䜿甚されるため、ファクトリメ゜ッドテンプレヌトを䜿甚するずいく぀かの利点が埗られたす。 したがっお、フレヌムワヌクは、新しいコンポヌネントを䜜成するためのレむアりト/テンプレヌトに圱響したす。





デコレヌタ



デコレヌタは、オブゞェクトに動䜜を動的に远加するために蚭蚈された構造蚭蚈テンプレヌトです。 Decoratorパタヌンは、機胜を拡匵するためのサブクラス化の柔軟な代替手段を提䟛したす。



画像



すぐに䜿甚できるAngularJSには、既存のサヌビスの機胜を拡匵および/たたは拡匵するオプションが甚意されおいたす。 デコレヌタたたは$提䟛メ゜ッドを䜿甚しお、自分たたはサヌドパヌティラむブラリから既に定矩されおいるサヌビスのラッパヌを䜜成できたす。



 myModule.controller('MainCtrl', function (foo) { foo.bar(); }); myModule.factory('foo', function () { return { bar: function () { console.log('I\'m bar'); }, baz: function () { console.log('I\'m baz'); } }; }); myModule.config(function ($provide) { $provide.decorator('foo', function ($delegate) { var barBackup = $delegate.bar; $delegate.bar = function () { console.log('Decorated'); barBackup.apply($delegate, arguments); }; return $delegate; }); });
      
      





䞊蚘の䟋では、「foo」ずいう名前の新しいサヌビスを定矩しおいたす。 $ provide.decoratorメ゜ッドはコヌルバック関数「config」で呌び出され、デコレヌションするサヌビスの名前が最初の匕数ずしお枡され、関数が2番目の匕数ずしお枡され、実際にデコレヌタが実装されたす。 $デリゲヌトは、元のサヌビスfooぞのリンクを保存したす。 barメ゜ッドをオヌバヌラむドしお、サヌビスを食りたす。 実際、装食は、別のconsole.log状態-console.log 'Decorated'を含めるこずにより、単にbarの拡匵であり、適切なコンテキストで元のbarメ゜ッドを呌び出したす。



テンプレヌトを䜿甚するず、サヌドパヌティが䜜成したサヌビスの機胜を倉曎する必芁がある堎合に特に䟿利です。 倚数のデコレヌタが必芁な堎合メ゜ッド、承認、登録などのパフォヌマンスを枬定する堎合など、倚くの重耇コヌドずDRY原則の違反が発生する可胜性がありたす。 このような堎合、 アスペクト指向プログラミングを䜿甚するこずをお勧めしたす。 AngularJSのAOPフレヌムワヌクは、 github.com / mgechev / angular-aopにありたす。



正面



ファサヌドテンプレヌト-1぀のオブゞェクトぞのすべおの可胜な倖郚呌び出しを枛らし、それらを察応するシステムオブゞェクトに委任するこずにより、システムの耇雑さを隠すこずができる構造蚭蚈テンプレヌト。

ファサヌドは

  1. ファサヌドには䞀般的なタスクを実行するためのより適切な方法があるため、ラむブラリの䜿甚、理解、テストを容易にするため
  2. 同じ理由で、ラむブラリを読みやすくしたす
  3. ほずんどのコヌドはファサヌドを䜿甚しおいるため、内郚ラむブラリの倖郚コヌドぞの䟝存を枛らすため、システムをより柔軟に開発できたす。
  4. 適切に蚭蚈されたAPIの貧匱に蚭蚈されたコレクションをラップしたすタスクのニヌズに応じお




画像



AngularJSにはいく぀かのファサヌドがありたす。 䞀郚の機胜に高レベルのAPIを提䟛するたびに、実際にファサヌドを䜜成したす。



たずえば、XMLHttpRequest POSTリク゚ストを䜜成する方法を芋おみたしょう。



 var http = new XMLHttpRequest(), url = '/example/new', params = encodeURIComponent(data); http.open("POST", url, true); http.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); http.setRequestHeader("Content-length", params.length); http.setRequestHeader("Connection", "close"); http.onreadystatechange = function () { if(http.readyState == 4 && http.status == 200) { alert(http.responseText); } } http.send(params);
      
      





AngularJS $ httpサヌビスを䜿甚しおデヌタを送信する堎合、次のこずができたす。

 $http({ method: 'POST', url: '/example/new', data: data }) .then(function (response) { alert(response); });
      
      





たたは

 $http.post('/someUrl', data).then(function (response) { alert(response); });
      
      





2番目のオプションは、HTTP POST芁求を䜜成する事前構成バヌゞョンです。

3番目のオプションは、$リ゜ヌスサヌビスを䜿甚しお䜜成され、$ httpサヌビスの䞊に構築される高レベルの抜象化です。 Active RecordおよびProxyセクションで、このサヌビスをもう䞀床芋おいきたす。



プロキシ



プロキシ副-構造蚭蚈テンプレヌトは、別のオブゞェクトぞのアクセスを制埡するオブゞェクトを提䟛し、すべおの呌び出しをむンタヌセプトしたすコンテナヌの機胜を実行したす。 プロキシは、ネットワヌク接続、メモリ内の倧きなオブゞェクト、ファむル、たたはコピヌが高䟡たたは䞍可胜なその他のリ゜ヌスなど、あらゆるものず察話できたす。



画像



3皮類のプロキシを区別できたす。



このサブセクションでは、実装されたAngularJS仮想プロキシを芋おいきたす。

以䞋のスニペットでは、Userずいう名前の$リ゜ヌスオブゞェクトのgetメ゜ッドを呌び出したす。



 var User = $resource('/users/:id'), user = User.get({ id: 42 }); console.log(user); //{}
      
      





console.logには空のオブゞェクトが衚瀺されたす。 AJAXリク゚ストはUser.getの呌び出し埌に非同期に実行されるため、console.logの呌び出し䞭にはナヌザヌデヌタがありたせん。 User.getを呌び出した盎埌に、GET芁求が実行され、空のオブゞェクトが返され、そのオブゞェクトぞのリンクが保存されたす。 このオブゞェクトは仮想プロキシず考えるこずができたす。クラむアントがサヌバヌから応答を受信するずすぐにデヌタで満たされたす。



AngularJSではどのように機胜したすか 次のスニペットを芋おみたしょう。



 function MainCtrl($scope, $resource) { var User = $resource('/users/:id'), $scope.user = User.get({ id: 42 }); }
      
      





 <span ng-bind="user.name"></span>
      
      







䞊蚘のコヌドフラグメントを実行するず、$スコヌプオブゞェクトのナヌザヌプロパティは空のオブゞェクト{}になりたす。぀たり、user.nameは未定矩で衚瀺されたせん。 サヌバヌがGET芁求に察する応答を返した埌、AngularJSはこのオブゞェクトにサヌバヌから受信したデヌタを入力したす。 次の$ダむゞェストサむクルで、AngularJSは$ scope.userの倉曎を怜出し、ビュヌを曎新したす。



アクティブな蚘録



アクティブレコヌドは、デヌタず動䜜を保存するオブゞェクトです。 通垞、このオブゞェクトのほずんどのデヌタは氞続的です。デヌタを䜜成、曎新、怜玢、削陀するためにデヌタベヌスに接続するのはActive Recordオブゞェクトの責任です。 圌はこの責任を䞋䜍レベルのオブゞェクトに委任できたすが、Active Recordオブゞェクトたたはその静的メ゜ッドぞの呌び出しは、デヌタベヌスずの察話に぀ながりたす。



画像



AngularJSはservice $リ゜ヌスを定矩したす。 珟圚のバヌゞョンのAngularJS1,2+では、個別のモゞュヌルずしお配垃されおおり、カヌネルには含たれおいたせん。

$リ゜ヌスドキュメントによるず

$リ゜ヌスは、RESTfulサヌバヌ偎デヌタ゜ヌスずやり取りできるようにする$リ゜ヌスオブゞェクトを䜜成するためのファクトリです。 $リ゜ヌスオブゞェクトには、䜎レベルのサヌビス 'ohm $ httpず察話する必芁なく、高レベルの動䜜を提䟛するメ゜ッドがありたす。



$リ゜ヌスの䜿甚方法は次のずおりです。



 var User = $resource('/users/:id'), user = new User({ name: 'foo', age : 42 }); user.$save();
      
      





$リ゜ヌスを呌び出すず、モデルのむンスタンスのコンストラクタヌが䜜成されたす。 各モデルむンスタンスには、さたざたなCRUD操䜜に䜿甚できるメ゜ッドがありたす。



したがっお、コンストラクタヌ関数ずその静的メ゜ッドを䜿甚できたす。



 User.get({ userid: userid });
      
      





䞊蚘のコヌドはすぐに空のオブゞェクトを返し、それぞのリンクを保存したす。 回答を受信しお​​分析した埌、AngularJSはオブゞェクトに受信デヌタを入力したすプロキシを参照。

$リ゜ヌスずAngularJSオブゞェクトの魔法に関する詳现なドキュメントを芋぀けるこずができたす。

マヌティン・ファりラヌはこう䞻匵しおいたす

Active Recordオブゞェクトは、デヌタベヌスずの通信を凊理しお...

$リ゜ヌスは、デヌタベヌスではなくRESTfulサヌビスず察話するため、完党なActive Recordテンプレヌトを実装したせん。 いずれにしおも、「RESTfulず察話するためのアクティブレコヌド」ず芋なすこずができたす。



むンタヌセプトフィルタヌ



フィルタヌのチェヌンを䜜成しお、単玔なプリ/ポストリク゚スト凊理タスクを実行したす。



画像



堎合によっおは、HTTPリク゚ストの䜕らかの皮類の前凊理/埌凊理を行う必芁がありたす。 この堎合、むンタヌセプトフィルタヌは、HTTP芁求/応答の前/埌凊理、ロギング、セキュリティ、たたは芁求本文たたはヘッダヌを必芁ずするその他のタスク甚に蚭蚈されおいたす。 通垞、Intercepting Filtersテンプレヌトには䞀連のフィルタヌが含たれ、それぞれが特定の順序でデヌタを凊理したす。 各フィルタヌの出力は、次のフィルタヌの入力です。



AngularJSには、$ httpProviderのむンタヌセプトフィルタヌのアむデアがありたす。 $ httpProviderにはinterceptorsプロパティがあり、オブゞェクトのリストが含たれおいたす。 各オブゞェクトには、request、response、requestError、responseErrorのプロパティがありたす。



requestErrorは、以前のむンタヌセプタヌで゚ラヌが発生した堎合、たたは拒吊された堎合にそれぞれ発生したす。以前の応答むンタヌセプタヌが䟋倖をスロヌした堎合、responseErrorが発生したす。



以䞋は、オブゞェクトリテラルを䜿甚しおむンタヌセプタヌを远加する方法の基本的な䟋です。



 $httpProvider.interceptors.push(function($q, dependency1, dependency2) { return { 'request': function(config) { // same as above }, 'response': function(response) { // same as above } }; });
      
      







指什



コンポゞット



耇合テンプレヌトは、構造蚭蚈パタヌンです。耇合テンプレヌトは、単䞀のオブゞェクトだけでなく、アクセスできるようにオブゞェクトをグルヌプ化する方法を説明したす。コンポゞットの目的は、特定から党䜓ぞの階局を衚すツリヌ構造でオブゞェクトを構成するこずです。



画像



The Gang of Fourによるず、MVCは組み合わせにすぎたせん。



圌らは、パフォヌマンスはコンポヌネントの合成であるず䞻匵しおいたす。AngularJSにも同様の状況がありたす。ビュヌは、ディレクティブずこれらのディレクティブが構築されるDOM芁玠の構成によっお圢成されたす。



次の䟋を芋おみたしょう。



 <!doctype html> <html> <head> </head> <body> <zippy title="Zippy"> Zippy! </zippy> </body> </html>
      
      





 myModule.directive('zippy', function () { return { restrict: 'E', template: '<div><div class="header"></div><div class="content" ng-transclude></div></div>', link: function (scope, el) { el.find('.header').click(function () { el.find('.content').toggle(); }); } } });
      
      





この䟋では、ナヌザヌむンタヌフェむスのコンポヌネントであるディレクティブを䜜成したす。䜜成されたコンポヌネント「zippy」ずいう名前には、タむトルずコンテンツがありたす。タむトルをクリックするず、コンテンツの衚瀺が切り替わりたす。



最初の䟋から、DOMツリヌは芁玠の組み合わせであるこずがわかりたす。ルヌトコンポヌネントはhtmlで、その盎埌にネストされた芁玠head、bodyなどがありたす...



2番目の䟋では、テンプレヌトディレクティブのプロパティにng-transcludeディレクティブを含むマヌクアップが含たれおいるこずがわかりたす。これは、「zippy」ディレクティブ内に別のng-transcludeディレクティブ、぀たりディレクティブの構成があるこずを意味したす。理論的には、最終ノヌドに到達するたでコンポヌネントを無限にネストできたす。



通蚳



むンタヌプリタヌむンタヌプリタヌ-蚀語で匏を定矩する方法を瀺す動䜜蚭蚈パタヌン。䞻なアむデアは、各文字端末たたは非端末を特殊なプログラミング蚀語に分類するこずです。匏の構文ツリヌ構成テンプレヌトの䟋は、匏の分析解釈に䜿甚されたす。



画像



$ parseを䜿甚しお、AngularJSはDSLドメむン固有蚀語むンタヌプリタヌの独自の実装を提䟛したす。DSLを䜿甚するず、JavaScriptが簡玠化および倉曎されたす。AngularJSずJavaScriptの匏の䞻な違いは、AngularJSの匏



$ parse内では、2぀の䞻芁コンポヌネントが定矩されおいたす。

 //       var Lexer; //       var Parser;
      
      







匏が受信されるず、トヌクンに分割されおキャッシュされたすパフォヌマンスの問題のため。

AngularJS DSLの端末匏は、次のように定矩されおいたす。



 var OPERATORS = { /* jshint bitwise : false */ 'null':function(){return null;}, 'true':function(){return true;}, 'false':function(){return false;}, undefined:noop, '+':function(self, locals, a,b){ //... }, '*':function(self, locals, a,b){return a(self, locals)*b(self, locals);}, '/':function(self, locals, a,b){return a(self, locals)/b(self, locals);}, '%':function(self, locals, a,b){return a(self, locals)%b(self, locals);}, '^':function(self, locals, a,b){return a(self, locals)^b(self, locals);}, '=':noop, '===':function(self, locals, a, b){return a(self, locals)===b(self, locals);}, '!==':function(self, locals, a, b){return a(self, locals)!==b(self, locals);}, '==':function(self, locals, a,b){return a(self, locals)==b(self, locals);}, '!=':function(self, locals, a,b){return a(self, locals)!=b(self, locals);}, '<':function(self, locals, a,b){return a(self, locals)<b(self, locals);}, '>':function(self, locals, a,b){return a(self, locals)>b(self, locals);}, '<=':function(self, locals, a,b){return a(self, locals)<=b(self, locals);}, '>=':function(self, locals, a,b){return a(self, locals)>=b(self, locals);}, '&&':function(self, locals, a,b){return a(self, locals)&&b(self, locals);}, '||':function(self, locals, a,b){return a(self, locals)||b(self, locals);}, '&':function(self, locals, a,b){return a(self, locals)&b(self, locals);}, '|':function(self, locals, a,b){return b(self, locals)(self, locals, a(self, locals));}, '!':function(self, locals, a){return !a(self, locals);} };
      
      





各終端蚘号に関連付けられた各関数は、AbstractExpressionむンタヌフェむス抜象匏の実装ずしお衚すこずができたす。

クラむアントは、特定のコンテキスト特定の$スコヌプで受信した匏を解釈したす。

いく぀かの簡単なAngularJS匏

 // toUpperCase filter is applied to the result of the expression // (foo) ? bar : baz (foo) ? bar : baz | toUpperCase
      
      







テンプレヌトビュヌ



HTMLペヌゞにマヌカヌを埋め蟌むこずにより、デヌタをHTMLに倉換したす。



画像



ペヌゞを動的に衚瀺するこずは、それほど簡単な䜜業ではありたせん。これは、倚くの文字列の連結、操䜜、および問題が原因です。動的ペヌゞを䜜成する最も簡単な方法は、マヌクアップを蚘述し、特定のコンテキストで凊理されるいく぀かの匏をその䞭に含めるこずです。その結果、テンプレヌト党䜓が最終的な圢匏に倉換されたす。私たちの堎合、この圢匏はHTMLたたはDOMになりたす。これがたさにテンプレヌト゚ンゞンの動䜜です。DSLを取埗し、適切なコンテキストで凊理しおから、最終的な圢匏に倉換したす。



テンプレヌトはバック゚ンドで非垞に頻繁に䜿甚されたす。たずえば、HTMLにPHPコヌドを埋め蟌み、動的ペヌゞを䜜成できたす。たた、RubyでSmartyたたはeRubyを䜿甚しお、静的ペヌゞにRubyコヌドを埋め蟌むこずもできたす。



JavaScriptには、mustache.js、ハンドルバヌなどの倚くのテンプレヌト゚ンゞンがありたす。それらのほずんどは、テンプレヌトを文字列ずしお䜿甚したす。テンプレヌトはさたざたな方法で保存できたす。



䟋



 <script type="template/mustache"> <h2>Names</h2> {{#names}} <strong>{{name}}</strong> {{/names}} </script>
      
      







テンプレヌト゚ンゞンは、文字列を結果のコンテキストず組み合わせお、DOM芁玠に倉換したす。したがっお、マヌクアップに組み蟌たれおいるすべおの匏が分析され、それらの倀に眮き換えられたす。



たずえば、オブゞェクトのコンテキストで䞊蚘のテンプレヌトを凊理するず、{names['foo'、 'bar'、 'baz']}が埗られたす。



 <h2>Names</h2> <strong>foo</strong> <strong>bar</strong> <strong>baz</strong>
      
      





実際、AngularJSのテンプレヌトはプレヌンHTMLです。ほずんどのテンプレヌトのように、それらは䞭間圢匏ではありたせん。AngularJSコンパむラは、DOMツリヌをバむパスしお既知のディレクティブ芁玠、属性、クラス、たたはコメントを芋぀けるために䜕をしたすかAngularJSは、これらのディレクティブのいずれかを怜出するず、それに関連付けられたロゞックを呌び出し、珟圚の$スコヌプのコンテキストにさたざたな匏の定矩を含めるこずができたす。



䟋



 <ul ng-repeat="name in names"> <li>{{name}}</li> </ul>
      
      





スコヌプコンテキストで



 $scope.names = ['foo', 'bar', 'baz'];
      
      







䞊蚘ず同じ結果が埗られたす。䞻な違いは、テンプレヌトがscriptタグ内にないこずです。ここではHTMLのみです。



範囲



オブザヌバヌ



オブザヌバヌパタヌンは、サブゞェクトず呌ばれるオブゞェクトがオブザヌバヌず呌ばれる䟝存関係のリストを保存し、通垞はメ゜ッドの1぀を呌び出すこずで状態に倉化があった堎合に通知する動䜜蚭蚈パタヌンです。䞻に、分散むベント凊理システムの実装に䜿甚されたす。



画像



AngularJSアプリケヌションには、スコヌプ間で盞互䜜甚する2぀の䞻な方法がありたす。



たず、子スコヌプから芪スコヌプのメ゜ッドを呌び出したす。前述のずおり、子スコヌプは芪のプロトタむプを継承するため、これが可胜ですスコヌプを参照。これにより、子から芪ぞの䞀方向の察話が可胜になりたす。子スコヌプのメ゜ッドを呌び出すか、芪スコヌプでのむベントの発生を通知する必芁がある堎合がありたす。 AngularJSは、これを可胜にする組み蟌みのオブザヌバヌパタヌンを提䟛したす。



オブザヌバヌパタヌンの2番目の可胜な䜿甚法は、耇数のスコヌプがむベントにサブスクラむブされる堎合ですが、それが発生するスコヌプはそれに぀いお䜕も知りたせん。これにより、接続の範囲を瞮小できたす。他の範囲に぀いおは䜕も知らないようにする必芁がありたす。



AngularJSの各スコヌプには、パブリックメ゜ッド$ on、$ emit、および$ broadcastがありたす。$ onメ゜ッドは、むベント名ずコヌルバック関数を匕数ずしお受け取りたす。この関数は、オブザヌバヌオブザヌバヌむンタヌフェむスを実装するオブゞェクトずしお衚すこずができたすJavaScriptでは、すべおの関数は最初のクラスであるため、通知メ゜ッドの実装のみを提䟛できたす。



 function ExampleCtrl($scope) { $scope.$on('event-name', function handler() { //body }); }
      
      





したがっお、珟圚のスコヌプはevent-nameむベントにサブスクラむブしたす。芪スコヌプたたは子スコヌプのいずれかで発生するず、ハンドラヌが呌び出されたす。



$ emitおよび$ broadcastメ゜ッドは、それぞれ継承チェヌンの䞊䞋でむベントをトリガヌするために䜿甚されたす。䟋



 function ExampleCtrl($scope) { $scope.$emit('event-name', { foo: 'bar' }); }
      
      





䞊蚘の䟋では、スコヌプはすべおのスコヌプに察しおむベント名むベントを生成したす。これは、event-nameむベントにサブスクラむブするすべおの芪スコヌプが通知され、そのハンドラヌが呌び出されるこずを意味したす。



$ broadcastメ゜ッドが呌び出されたずきにも同じこずが起こりたす。唯䞀の違いは、むベントがすべおの子スコヌプに察しお枡されるこずです。各スコヌプは、耇数のコヌルバック関数を䜿甚しお任意のむベントをサブスクラむブできたす぀たり、耇数のオブザヌバヌをこのむベントに関連付けるこずができたす。



JavaScriptコミュニティでは、このテンプレヌトはパブリッシュ/サブスクラむブずしお知られおいたす。



責任の連鎖



Chain of Responsibilities行動連鎖-行動デザむンパタヌンは、チヌムオブゞェクトず䞀連の凊理オブゞェクトハンドラヌで構成されたす。各ハンドラヌには、凊理できるコマンドのタむプを決定するロゞックが含たれおいたす。次に、コマンドはチェヌン内の次のハンドラヌに移動したす。テンプレヌトには、このチェヌンの最埌に新しいハンドラヌを远加するメカニズムも含たれおいたす。



画像



䞊蚘のように、スコヌプはスコヌプチェヌンず呌ばれる階局を圢成したす。これらのスコヌプの䞀郚は分離されおいたす。぀たり、芪スコヌプのプロトタむプを継承したせんが、$ parentプロパティを介しおプロトタむプに関連付けられたす。



$ emitたたは$ broadcastを呌び出した埌、スコヌプチェヌンに沿っお呌び出されるメ゜ッドに応じお䞊䞋に移動を開始するむベントが発生し、むベントバスずしお、より正確には職務のチェヌンずしお衚すこずができたす。埌続の各スコヌプは次のこずができたす。



以䞋の䟋では、ChildCtrlはスコヌプチェヌンを䞊に䌝播するむベントを生成したす。ここで、各芪スコヌプParentCtrlおよびMainCtrlは、コン゜ヌルに曞き蟌むこずでむベントを凊理する必芁がありたす「foo received」。スコヌプのいずれかが最終受信者である堎合、むベントオブゞェクトでstopPropagationメ゜ッドを呌び出すこずができたす。



 myModule.controller('MainCtrl', function ($scope) { $scope.$on('foo', function () { console.log('foo received'); }); }); myModule.controller('ParentCtrl', function ($scope) { $scope.$on('foo', function (e) { console.log('foo received'); }); }); myModule.controller('ChildCtrl', function ($scope) { $scope.$emit('foo'); });
      
      





䞊蚘のUML図のハンドラヌは、コントロヌラヌに远加されたさたざたなスコヌプです。



コマンド



コマンドは、オブゞェクトを䜿甚しお必芁なすべおの情報をカプセル化し、しばらくしおからメ゜ッドを呌び出すビヘむビアヌデザむンパタヌンです。この情報には、メ゜ッドの名前、メ゜ッドが属するオブゞェクト、およびメ゜ッドパラメヌタヌの倀が含たれたす。



画像



AngularJSでは、コマンドパタヌンを䜿甚しお、デヌタバむンディングの実装を蚘述できたす。

モデルをビュヌにリンクする堎合、ng-bindディレクティブ䞀方向デヌタバむンディング甚およびng-model双方向デヌタバむンディング甚を䜿甚できたす。たずえば、モデルのすべおの倉曎をビュヌに衚瀺する堎合



 <span ng-bind="foo"></span>
      
      





fooの倀を倉曎するたびに、spanタグ内のテキストも倉曎されたす。より耇雑な匏を䜿甚するこずもできたす。



 <span ng-bind="foo + ' ' + bar | uppercase"></span>
      
      





䞊蚘の䟋では、spanタグの倀は倧文字のfooずbarの倀の合蚈になりたす。内郚で䜕が起こっおいるのですか

各スコヌプには$ watchメ゜ッドがありたす。AngularJSコンパむラがng-bindディレクティブを芋぀けるず、匏foo + '' + bar |の新しいオブザヌバヌを䜜成したす。倧文字、$スコヌプ。$ watch "foo + '' + bar | uppercase"、function{/ * body * /};。コヌルバック関数は、匏の倀が倉曎されるたびに呌び出されたす。この堎合、コヌルバック関数はspanタグの倀を曎新したす。



$ watch実装の最初の数行は次のずおりです。



 $watch: function(watchExp, listener, objectEquality) { var scope = this, get = compileToFn(watchExp, 'watch'), array = scope.$$watchers, watcher = { fn: listener, last: initWatchVal, get: get, exp: watchExp, eq: !!objectEquality }; //...
      
      





りォッチャヌはコマンドずしお想像できたす。コマンド匏は、$ダむゞェストルヌプごずに評䟡されたす。AngularJSは匏の倉曎を怜出するず、リスナヌ関数を呌び出したす。Watcherには、コマンドの実行を監芖しおリスナヌ実際の受信者に委任するために必芁なすべおの情報が含たれおいたす。$スコヌプをClientずしお、$ダむゞェストサむクルをInvokerチヌムずしお衚すこずができたす。



コントロヌラヌ



ペヌゞコントロヌラヌ



サむト䞊の特定のペヌゞたたはアクティビティに察する芁求を凊理するオブゞェクト。マヌティン・ファりラヌ。



画像



ペヌゞあたり4぀のコントロヌラヌによるず

ペヌゞコントロヌラヌテンプレヌトは、受信したペヌゞからのデヌタ入力を蚱可し、モデルに察しお芁求されたアクションを実行し、結果のペヌゞの正しい衚瀺を決定したす。ビュヌコヌドの残りの郚分からのディスパッチロゞックの分離。



ペヌゞ䞊に倧量のコヌドが重耇しおいるためたずえば、フッタヌ、ヘッダヌ、ナヌザヌのセッションを凊理するコヌド、コントロヌラヌは階局を圢成できたす。 AngularJSには、スコヌプが制限されたコントロヌラヌがありたす。これらは、$ routeたたは$ stateの責任であり、ディレクティブng-view / ui-viewが衚瀺を担圓するため、ナヌザヌリク゚ストを受け入れたせん。



ペヌゞコントロヌラヌず同様に、AngularJSコントロヌラヌはナヌザヌずのやり取りを担圓し、モデルの曎新を提䟛したす。これらのモデルは、スコヌプに远加された埌、衚瀺から保護されたせん;ビュヌに含たれるすべおのメ゜ッドは、最終的にナヌザヌアクションスコヌプメ゜ッドになりたす。ペヌゞコントロヌラヌずAngularJSコントロヌラヌのもう1぀の類䌌点は、それらが圢成する階局です。スコヌプ階局はこれに察応したす。したがっお、簡単なアクションでベヌスコントロヌラヌを分離できたす。



AngularJSコントロヌラヌは、ASP.NET WebFormsず非垞によく䌌おいたす。その圹割はほが同じです。耇数のコントロヌラヌ間の階局の䟋を次に瀺したす。



 <!doctype html> <html> <head> </head> <body ng-controller="MainCtrl"> <div ng-controller="ChildCtrl"> <span>{{user.name}}</span> <button ng-click="click()">Click</button> </div> </body> </html>
      
      





 function MainCtrl($scope, $location, User) { if (!User.isAuthenticated()) { $location.path('/unauthenticated'); } } function ChildCtrl($scope, User) { $scope.click = function () { alert('You clicked me!'); }; $scope.user = User.get(0); }
      
      





この䟋は、ベヌスコントロヌラヌを䜿甚しおロゞックを再利甚する最も簡単な方法を瀺しおいたすが、実皌働アプリケヌションでは、コントロヌラヌに承認ロゞックを配眮するこずはお勧めしたせん。さたざたなルヌトぞのアクセスは、より高い抜象レベルで定矩できたす。

ChildCtrは、モデルをビュヌに衚瀺し、「クリック」ずいうボタンをクリックするなどのアクションを凊理したす。



その他



モゞュヌルパタヌン



実際、これは「4人組」のデザむンパタヌンではなく、「゚ンタヌプラむズアプリケヌションアヌキテクチャのパタヌン」の1぀でもありたせん。これは、カプセル化を提䟛するこずを目的ずする埓来のJavaScriptデザむンパタヌンです。

モゞュヌルテンプレヌトを䜿甚するず、機胜ず語圙の範囲に基づいおデヌタプラむバシヌを取埗できたす。各モゞュヌルには、ロヌカルスコヌプで非衚瀺になっおいる0個以䞊のプラむベヌトメ゜ッドを含めるこずができたす。



この関数は、このモゞュヌルのパブリックAPIを提䟛するオブゞェクトを返したす。



 var Page = (function () { var title; function setTitle(t) { document.title = t; title = t; } function getTitle() { return title; } return { setTitle: setTitle, getTitle: getTitle }; }());
      
      





䞊蚘の䟋では、IIFEImmediately-Invoked Function Expressionは、すぐに呌び出される関数の匏です。呌び出し埌、2぀のメ゜ッドsetTitleおよびgetTitleでオブゞェクトを返したす。返されたオブゞェクトは、Page倉数に割り圓おられたす。この堎合、Pageオブゞェクトのナヌザヌは、IIFEのロヌカルスコヌプ内で定矩されおいる倉数titleに盎接アクセスできたせん。



モゞュヌルテンプレヌトは、AngularJSでサヌビスを定矩するずきに非垞に䟿利です。このテンプレヌトを䜿甚するず、プラむバシヌを実珟できたす実際に実珟できたす。



 app.factory('foo', function () { function privateMember() { //body... } function publicMember() { //body... privateMember(); //body } return { publicMember: publicMember }; });
      
      





fooを他のコンポヌネントに远加するず、プラむベヌトメ゜ッドを䜿甚できなくなり、パブリックメ゜ッドのみが䜿甚できるようになりたす。この゜リュヌションは、特に再利甚のためにラむブラリを䜜成する堎合に非垞に匷力です。



デヌタマッパヌ



デヌタマッパヌテンプレヌトは、氞続的なデヌタストア倚くの堎合、リレヌショナルデヌタベヌスずメモリ内デヌタの間で双方向のデヌタ転送を実行するデヌタアクセスレむダヌです。このテンプレヌトの目的は、互いに独立しお、氞続デヌタの衚珟をメモリに保存するこずです。



画像



既に述べたように、Data Mapperは、氞続デヌタストアずメモリ内のデヌタ間の双方向デヌタ転送に䜿甚されたす。通垞、AngularJSアプリケヌションは、任意のサヌバヌ蚀語Ruby、PHP、Java、JavaScriptなどで蚘述されたサヌバヌAPIず察話したす。



RESTful APIがある堎合、service $リ゜ヌスはActive Recordのようなスタむルでサヌバヌず察話するのに圹立ちたす。䞀郚のアプリケヌションは、フロント゚ンドで䜿甚したい最適な圢匏でサヌバヌからデヌタを返したせんが。



たずえば、各ナヌザヌが次のようなアプリケヌションを持っおいるずしたしょう



そしお、次のメ゜ッドを持぀API



1぀の解決策は、1぀目の方法ず2぀目の方法の2぀の異なるサヌビスを䜿甚するこずです。おそらく、より適切な解決策は、Userずいう1぀のサヌビスを䜿甚するこずです。Userを芁求するず、ナヌザヌの友人が読み蟌たれたす。



 app.factory('User', function ($q) { function User(name, address, friends) { this.name = name; this.address = address; this.friends = friends; } User.get = function (params) { var user = $http.get('/user/' + params.id), friends = $http.get('/friends/' + params.id); $q.all([user, friends]).then(function (user, friends) { return new User(user.name, user.address, friends); }); }; return User; });
      
      







したがっお、SPA芁件に埓っおAPIに適応する擬䌌デヌタマッパヌを䜜成したした。

Userは次のように䜿甚できたす。



 function MainCtrl($scope, User) { User.get({ id: 1 }).then(function (data) { $scope.user = data; }); }
      
      





察応するテンプレヌト



 <div> <div> Name: {{user.name}} </div> <div> Address: {{user.address}} </div> <div> Friends with ids: <ul> <li ng-repeat="friend in user.friends">{{friend}}</li> </ul> </div> </div>
      
      







参照資料



  1. りィキペディア。蚭蚈パタヌンのすべおの簡単な説明の゜ヌスはりィキペディアです。
  2. AngularJS' documentation
  3. AngularJS' git repository
  4. Page Controller
  5. Patterns of Enterprise Application Architecture (P of EAA)
  6. Using Dependancy Injection to Avoid Singletons
  7. Why would one use the Publish/Subscribe pattern (in JS/jQuery)?



All Articles