ベストアンサー1
1. ページをデザインしてから変更しないでくださいDOM操作
jQuery では、ページを設計してから、それを動的にします。これは、jQuery が拡張を目的として設計され、その単純な前提から驚くほど成長してきたためです。
しかし、AngularJS では、アーキテクチャを念頭に置いて最初から始める必要があります。「DOM のこの部分があり、X を実行させたい」と考えることから始めるのではなく、何を達成したいかを考え、次にアプリケーションの設計に取り掛かり、最後にビューの設計に取り掛かる必要があります。
2. jQueryをAngularJSで拡張しない
同様に、jQuery が X、Y、Z を実行するという考えから始めないでください。そのため、モデルとコントローラー用に AngularJS を追加します。これは、始めたばかりのときは本当に魅力的です。そのため、新しい AngularJS 開発者には、少なくとも「Angular 方式」で作業することに慣れるまでは、jQuery をまったく使用しないことを常にお勧めしています。
ここやメーリング リストで、多くの開発者が 150 行または 200 行のコードからなる jQuery プラグインを使用して複雑なソリューションを作成し、それを一連のコールバックや$apply
s とともに AngularJS に貼り付けるのを見てきましたが、これはわかりにくく複雑です。しかし、最終的には動作するようになります。問題は、ほとんどの場合、jQuery プラグインはコードの一部で AngularJS に書き直すことができ、突然すべてが理解しやすく簡単になることです。
肝心なのは、解決策を考えるときはまず「AngularJS で考える」ことです。解決策が思いつかない場合はコミュニティに尋ねてください。それでも簡単な解決策が見つからない場合は、遠慮なくjQuery を使ってください。ただし、jQuery に頼らないでください。そうしないと、AngularJS をマスターできなくなります。
3. 常にアーキテクチャの観点から考える
まず知っておくべきシングルページアプリケーションこれらはアプリケーションです。Webページではありません。したがって、クライアント側開発者のように考えることに加えて、サーバー側開発者のように考える必要があります。アプリケーションを個別の拡張可能でテスト可能なコンポーネントに分割する方法を考える必要があります。
では、どうやってそれを実現するのでしょうか? どのように「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 バージョンでは、テンプレートを見た人は誰でも何が起こるかがわかります。開発チームの新しいメンバーが参加するたびに、このテンプレートを見て、 operatingというディレクティブがあることを知ること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)。サーバーサイド言語(ジャワにPHP の) おそらくこの概念はすでにご存知でしょうが、jQuery から来たクライアント側の人間にとっては、この概念は馬鹿げている、余計だ、流行に敏感だと思われるかもしれません。しかし、そうではありません。:-)
広い観点から見ると、DI とは、コンポーネントを非常に自由に宣言でき、他のコンポーネントからそのインスタンスを要求するだけで、それが付与されることを意味します。読み込み順序やファイルの場所などについて知る必要はありません。その威力はすぐには目に見えないかもしれませんが、1 つの (一般的な) 例、テストを挙げます。
アプリケーションでは、サーバー側ストレージを実装するサービスが必要で、休む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 操作が少ないほど、ディレクティブのテストが容易になり、スタイル設定が容易になり、将来変更しやすくなり、再利用や配布が容易になります。
AngularJS を初めて使う開発者の多くが、jQuery を大量に投入する場所としてディレクティブを使用しているのを目にします。言い換えると、彼らは「コントローラーで DOM 操作ができないから、そのコードをディレクティブに置こう」と考えているのです。確かにそれはずっと良いのですが、それでも間違っている場合が多いのです。
セクション 3 でプログラムしたロガーについて考えてみましょう。それをディレクティブに記述したとしても、やはり「Angular 方式」で実行する必要があります。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 は変更されないため、リファクタリングは簡単です。ディレクティブを変更することなく、テンプレートを好きなだけ変更できます。また、何を変更しても、テストは合格します。
うわっ!
では、ディレクティブが jQuery のような関数の単なる集合体ではないとしたら、ディレクティブとは何でしょうか? ディレクティブは実際にはHTML の拡張機能です。HTML が必要な処理を実行しない場合は、それを実行するディレクティブを記述し、それを HTML の一部であるかのように使用します。
言い換えれば、AngularJS がすぐに実行できないことがあれば、チームがそれを 、 、 などに適合させるにはどうすればよいかを考えngClick
ますngClass
。
まとめ
jQuery は使用しないでください。組み込むことすらしないでください。jQuery は足かせになります。jQuery で解決する方法がすでにわかっている問題に遭遇した場合は、 に手を伸ばす前に、$
AngularJS の範囲内でそれを解決する方法について考えてみてください。わからない場合は、質問してください。20 回中 19 回は、それを実行する最善の方法に jQuery は必要ありません。jQuery で解決しようとすると、より多くの作業が必要になります。