1. ページをデザインせず、DOM操作で変更する
jQuery では、ページを設計してから動的にします。これは、jQuery が拡張用に設計されており、その単純な前提から信じられないほど成長したためです。
しかし、AngularJS では、アーキテクチャを念頭に置いてゼロから始める必要があります。「DOM のこの部分があり、それを X にしたい」と考えることから始めるのではなく、達成したいことから始めて、アプリケーションの設計に取り掛かり、最後にビューの設計に取り掛かる必要があります。
2. AngularJS で jQuery を拡張しない
同様に、jQuery が X、Y、および Z を実行するという考えから始めないでください。そのため、モデルとコントローラー用にその上に AngularJS を追加します。始めたばかりの場合、これは本当に魅力的です。そのため、新しい AngularJS 開発者は、少なくとも「Angular のやり方」に慣れるまでは、jQuery をまったく使用しないことを常にお勧めします。
ここやメーリング リストで、多くの開発者が 150 行または 200 行のコードの jQuery プラグインを使用してこれらの精巧なソリューションを作成しているのを見てきました$apply
。しかし、彼らは最終的にそれを機能させます!問題は、ほとんどの場合、AngularJS で jQuery プラグインをコードの一部で書き直すことができ、突然すべてがわかりやすく簡単になることです。
要点は次のとおりです。解決するときは、まず「AngularJS で考えてください」。解決策が思いつかない場合は、コミュニティに質問してください。それでも簡単な解決策がない場合は、気軽に jQuery を利用してください。ただし、jQuery を松葉杖にしないでください。そうしないと、AngularJS をマスターできなくなります。
3. 常にアーキテクチャの観点から考える
最初に、シングルページ アプリケーションはアプリケーションであることを理解してください。それらはウェブページではありません。そのため、クライアント側の開発者のように考えるだけでなく、サーバー側の開発者のように考える必要があります。アプリケーションを個々の拡張可能でテスト可能なコンポーネントに分割する方法を考える必要があります。
では、どうやってそれを行うのですか?「AngularJS で考える」方法は? jQuery と対比して、いくつかの一般原則を次に示します。
見解は「公式記録」
jQuery では、プログラムでビューを変更します。ul
次のようなドロップダウン メニューを定義できます。
<ul class="main-menu">
<li class="active">
<a href="#/home">Home</a>
</li>
<li>
<a href="#/menu1">Menu 1</a>
<ul>
<li><a href="#/sm1">Submenu 1</a></li>
<li><a href="#/sm2">Submenu 2</a></li>
<li><a href="#/sm3">Submenu 3</a></li>
</ul>
</li>
<li>
<a href="#/home">Menu 2</a>
</li>
</ul>
jQuery では、アプリケーション ロジックで、次のような方法でアクティブ化します。
$('.main-menu').dropdownMenu();
ビューを見るだけでは、ここに機能があることはすぐにはわかりません。小規模なアプリケーションの場合、それで問題ありません。しかし、重要なアプリケーションの場合、物事はすぐに混乱し、保守が困難になります。
ただし、AngularJS では、ビューはビューベースの機能の公式記録です。ul
代わりに、宣言は次のようになります。
<ul class="main-menu" dropdown-menu>
...
</ul>
これら 2 つは同じことを行いますが、AngularJS バージョンでは、テンプレートを見ている人は、何が起こるべきかを知っています。開発チームの新しいメンバーが参加するたびに、彼女はこれを見て、それを操作するというディレクティブがあることを知ることができます。dropdownMenu
彼女は正しい答えを直観したり、コードをふるいにかけたりする必要はありません。ビューは、何が起こるべきかを教えてくれました。ずっときれい。
AngularJS を初めて使用する開発者は、次のような質問をよくします。特定の種類のすべてのリンクを見つけて、それらにディレクティブを追加するにはどうすればよいですか。私たちが返信すると、開発者はいつもびっくりします。あなたは違います。しかし、そうしない理由は、これは半分 jQuery、半分 AngularJS のようなものであり、良くないからです。ここでの問題は、開発者が AngularJS のコンテキストで「jQuery を実行」しようとしていることです。それでは決してうまくいきません。ビューは公式記録です。ディレクティブの外では (詳細は後述)、DOM を変更することは決してありません。また、ディレクティブはビューに適用されるため、意図は明確です。
覚えておいてください: 設計してからマークアップしないでください。設計してから設計する必要があります。
データバインディング
これは、AngularJS の最も優れた機能の 1 つであり、前のセクションで述べた種類の DOM 操作を行う必要がなくなります。AngularJS はビューを自動的に更新するので、その必要はありません! jQuery では、イベントに応答してからコンテンツを更新します。何かのようなもの:
$.ajax({
url: '/myEndpoint.json',
success: function ( data, status ) {
$('ul#log').append('<li>Data Received!</li>');
}
});
次のようなビューの場合:
<ul class="messages" id="log">
</ul>
懸念事項の混合とは別に、前に述べた意図を示すという同じ問題もあります。しかし、もっと重要なことは、DOM ノードを手動で参照して更新する必要があったことです。また、ログ エントリを削除する場合は、そのためにも DOM に対してコードを作成する必要があります。DOM とは別にロジックをテストするにはどうすればよいでしょうか? プレゼンテーションを変更したい場合はどうすればよいでしょうか。
これは少し面倒で、些細な虚弱です。しかし、AngularJS では、これを行うことができます。
$http( '/myEndpoint.json' ).then( function ( response ) {
$scope.log.push( { msg: 'Data Received!' } );
});
ビューは次のようになります。
<ul class="messages">
<li ng-repeat="entry in log">{{ entry.msg }}</li>
</ul>
しかし、さらに言えば、ビューは次のようになります。
<div class="messages">
<div class="alert" ng-repeat="entry in log">
{{ entry.msg }}
</div>
</div>
また、順序付けられていないリストを使用する代わりに、Bootstrap アラート ボックスを使用しています。そして、コントローラのコードを変更する必要はありませんでした! しかし、もっと重要なことは、ログがどこでどのように更新されても、ビューも変化するということです。自動的。きちんとした!
ここでは示していませんが、データ バインディングは双方向です。したがって、これらのログ メッセージは、次のようにするだけで、ビューで編集することもできます<input ng-model="entry.msg" />
。そして、多くの喜びがありました。
個別のモデル層
jQuery では、DOM はモデルのようなものです。しかし、AngularJS では、ビューから完全に独立して、任意の方法で管理できる別のモデル レイヤーがあります。これは、上記のデータ バインディングに役立ち、懸念事項の分離を維持し、はるかに優れたテスト容易性をもたらします。この点については他の回答が言及しているので、そのままにしておきます。
関心事の分離
そして、上記のすべては、この包括的なテーマに結びついています。あなたの見解は、(ほとんどの場合)起こるべきことの公式記録として機能します。モデルはデータを表します。再利用可能なタスクを実行するサービス層があります。DOM 操作を行い、ビューをディレクティブで拡張します。コントローラーですべてを接着します。これは他の回答でも言及されており、追加する唯一のことはテスト容易性に関するものであり、これについては以下の別のセクションで説明します。
依存性注入
関心の分離を支援するのが依存性注入(DI) です。サーバー側の言語 ( JavaからPHPまで) を使用している場合は、おそらくこの概念に既に精通しているでしょうが、jQuery を使用しているクライアント側の言語を使用している場合、この概念はばかげているものから余分なもの、流行に敏感なものまで、あらゆるものに見える可能性があります。 . しかし、そうではありません。:-)
広い観点から見ると、DI はコンポーネントを非常に自由に宣言できることを意味し、他のコンポーネントからインスタンスを要求するだけで許可されます。読み込み順序やファイルの場所などについて知る必要はありません。その威力はすぐにはわからないかもしれませんが、(一般的な) 例を 1 つだけ紹介します: テストです。
私たちのアプリケーションでは、REST API を介してサーバー側のストレージを実装するサービスが必要であり、アプリケーションの状態によってはローカル ストレージも必要です。コントローラーでテストを実行する場合、サーバーと通信する必要はありません。コントローラーをテストしているためです。元のコンポーネントと同じ名前のモック サービスを追加するだけで、インジェクターはコントローラーが偽のサービスを自動的に取得するようにします。コントローラーは違いを認識していませんし、認識する必要もありません。
テストといえば…
4. テスト駆動開発 -常に
これは実際にはアーキテクチャに関するセクション 3 の一部ですが、非常に重要であるため、独自のトップレベル セクションにしています。
あなたが見たり、使用したり、書いたりした多くの jQuery プラグインのうち、テスト スイートが付属しているものはいくつありますか? jQueryはそれにあまり適していないため、それほど多くはありません。しかし、AngularJS はそうです。
jQuery でテストする唯一の方法は、多くの場合、テストで DOM 操作を実行できるサンプル/デモ ページを使用してコンポーネントを個別に作成することです。そのため、コンポーネントを個別に開発してから、アプリケーションに統合する必要があります。なんて不便!多くの場合、jQuery を使用して開発する場合、テスト駆動開発ではなく反復開発を選択します。そして、誰が私たちを責めることができますか?
しかし、懸念事項が分離されているため、AngularJS で繰り返しテスト駆動開発を行うことができます。たとえば、現在のルートが何であるかをメニューに示す非常に単純なディレクティブが必要だとしましょう。アプリケーションのビューで必要なものを宣言できます。
<a href="/hello" when-active>Hello</a>
when-active
さて、存在しないディレクティブのテストを書くことができます:
it( 'should add "active" when the route changes', inject(function() {
var elm = $compile( '<a href="/hello" when-active>Hello</a>' )( $scope );
$location.path('/not-matching');
expect( elm.hasClass('active') ).toBeFalsey();
$location.path( '/hello' );
expect( elm.hasClass('active') ).toBeTruthy();
}));
テストを実行すると、失敗することが確認できます。ここで、ディレクティブを作成する必要があります。
.directive( 'whenActive', function ( $location ) {
return {
scope: true,
link: function ( scope, element, attrs ) {
scope.$on( '$routeChangeSuccess', function () {
if ( $location.path() == element.attr( 'href' ) ) {
element.addClass( 'active' );
}
else {
element.removeClass( 'active' );
}
});
}
};
});
テストに合格し、メニューは要求どおりに実行されます。私たちの開発は、反復的かつテスト駆動型です。邪悪なクール。
5. 概念的には、ディレクティブはjQuery にパッケージ化されていません
「ディレクティブでのみ DOM 操作を行う」という言葉をよく耳にします。これは必需品です。敬意を持って扱ってください!
しかし、もう少し深く掘り下げてみましょう...
一部のディレクティブは、既にビューにあるものを装飾するだけであり ( と考えてngClass
ください)、そのため、DOM 操作をすぐに実行してから基本的に完了する場合があります。しかし、ディレクティブが「ウィジェット」のようなもので、テンプレートがある場合は、関心の分離も尊重する必要があります。つまり、テンプレートも、リンクおよびコントローラー関数の実装から大きく独立したままにする必要があります。
AngularJS には、これを非常に簡単にするツール一式が付属しています。クラスをngClass
動的に更新できます。ngModel
双方向のデータ バインディングを許可します。ngShow
プログラムで要素をngHide
表示または非表示にします。さらに多くの - 私たちが自分で書いたものを含みます。つまり、 DOM を操作しなくても、あらゆる種類の素晴らしい機能を実行できます。DOM の操作が少ないほど、ディレクティブのテスト、スタイル設定、将来の変更が容易になり、再利用と配布が容易になります。
多くの開発者がディレクティブを jQuery の束を投入する場所として使用している AngularJS に慣れていないのを目にします。つまり、「コントローラーでDOM操作ができないので、そのコードをディレクティブに入れよう」と考えているのです。それは確かにはるかに優れていますが、それでもなお間違っていることがよくあります。
セクション 3 でプログラムしたロガーを考えてみてください。それをディレクティブに入れても、「Angular Way」でやりたいと思います。それでもDOM 操作は必要ありません。DOM の操作が必要になることはよくありますが、思ったよりもめったにありません。アプリケーションの任意の場所でDOM 操作を行う前に、本当に必要かどうか自問してください。もっと良い方法があるかもしれません。
これは、私が最も頻繁に目にするパターンを示す簡単な例です。切り替え可能なボタンが必要です。(注: この例は、まったく同じ方法で解決されるより複雑なケースを表すために少し不自然で、冗長です。)
.directive( 'myDirective', function () {
return {
template: '<a class="btn">Toggle me!</a>',
link: function ( scope, element, attrs ) {
var on = false;
$(element).click( function () {
on = !on;
$(element).toggleClass('active', on);
});
}
};
});
これにはいくつか問題があります。
- まず、jQuery は必要ありませんでした。ここでは、jQuery を必要とすることは何もしていません。
- 第 2 に、ページに既に jQuery があるとしても、ここでそれを使用する理由はありません。簡単に使用でき
angular.element
、jQuery を持たないプロジェクトにドロップしても、コンポーネントは引き続き機能します。
- 第 3 に、このディレクティブが機能するために jQueryが必要であると仮定しても、jqLite (
angular.element
) は、ロードされていれば常にjQuery を使用します。したがって、 を使用する必要はありません$
- を使用するだけangular.element
です。
- 4 つ目は、3 つ目と密接に関連しており、jqLite 要素をラップする必要がないこと
$
です。関数にelement
渡される要素は、すでにjQuery 要素になっています。link
- そして 5 つ目は、前のセクションで述べたように、なぜテンプレートをロジックに混ぜているのでしょうか?
このディレクティブは (非常に複雑な場合でも!) より簡単に次のように書き換えることができます。
.directive( 'myDirective', function () {
return {
scope: true,
template: '<a class="btn" ng-class="{active: on}" ng-click="toggle()">Toggle me!</a>',
link: function ( scope, element, attrs ) {
scope.on = false;
scope.toggle = function () {
scope.on = !scope.on;
};
}
};
});
繰り返しますが、テンプレートはテンプレートに含まれているため、あなた (またはユーザー) は、必要なスタイルに合ったものに簡単に交換でき、ロジックに触れる必要はありません。再利用性 - ブーム!
そして、テストなど、他のすべての利点がまだあります - 簡単です! テンプレートに何が含まれていても、ディレクティブの内部 API は決して変更されないため、リファクタリングは簡単です。ディレクティブに触れずに、テンプレートを好きなだけ変更できます。何を変更しても、テストは成功します。
w00t!
では、ディレクティブが jQuery のような関数の単なるコレクションではない場合、それらは何なのでしょうか? ディレクティブは、実際には HTML の拡張です。HTML が必要な処理を行わない場合は、それを行うディレクティブを作成し、それを HTML の一部であるかのように使用します。
ngClick
別の言い方をすれば、AngularJS がすぐに使用できるものではない場合、チームがそれをどのように達成してngClass
.
概要
jQuery も使用しないでください。含めないでください。それはあなたを引き止めます。そして、jQuery で解決する方法を既に知っていると思われる問題に遭遇した場合は、 に到達する前に$
、AngularJS の範囲内でそれを行う方法を考えてみてください。わからないなら聞く!20 回中 19 回は、それを行う最善の方法は jQuery を必要とせず、jQuery で解決しようとすると、より多くの作業が必要になります。