"jQueryのバックグラウンドを持っている場合は、AngularJS"で考える?

例えば、私がjQueryでクライアントサイドのアプリケーションを開発することに慣れていて、今度はAngularJSを使い始めようとしているとします。必要なパラダイムシフトを説明していただけますか?ここでは、答えを導き出すのに役立ちそうな質問をいくつか紹介します。

  • クライアントサイドのWebアプリケーションを異なる方法で設計し、デザインするにはどうすればいいですか?最大の違いは何ですか?
  • 何をやめて、何を始めて、何を使うべきですか?
  • サーバー側で考慮すべきことや制限事項はありますか?

私はjQueryAngularJSの詳細な比較を求めているわけではありません。

ソリューション

1.ページを設計してからDOM操作で変更するのはやめよう

jQueryでは、ページを設計してから、それを動的にする。これは、jQueryが拡張のために設計され、その単純な前提から信じられないほど成長したからです。 しかし、AngularJSでは、アーキテクチャを考慮して一から始めなければなりません。DOMのこの部分を持っていて、それにXをさせたい」と考えて始めるのではなく、達成したいことから始めて、アプリケーションを設計し、最後にビューを設計しなければなりません。

2.AngularJSでjQueryを補強するな

同様に、「jQueryはX、Y、Zの機能を持っているので、モデルとコントローラのためにAngularJSを追加しよう」という考えで始めないようにしましょう。これは、始めたばかりの時には非常に魅力的なことです。だからこそ、新しいAngularJS開発者には、少なくとも「Angular Way」での作業に慣れるまでは、jQueryを一切使用しないことを常に勧めています。 このサイトやメーリングリストで、多くの開発者が150~200行のコードのjQueryプラグインを使って手の込んだソリューションを作り、コールバックや「$apply」のコレクションを使ってAngularJSに糊付けしているのを見てきましたが、これらは混乱して複雑です。問題は、ほとんどの場合、jQueryプラグインをAngularJSで書き直せば、コードの数分の1で済み、突然すべてが理解できて簡単になるということです。 解決策を考えるときは、まず「AngularJSで考える」、解決策が思い浮かばないときはコミュニティに聞く、それでも簡単な解決策がないときは、*遠慮なくjQueryに手を伸ばす、ということです。しかし、jQueryを松葉づえのように使わないようにしないと、AngularJSをマスターすることはできません。

3.常にアーキテクチャの観点から考える

まず、「シングルページアプリケーション」2は、「アプリケーション」であることを知ってください。ウェブページではありません。ですから、クライアントサイドの開発者のように考えることに加えて、*サーバーサイドの開発者のように考える必要があります。アプリケーションを、個々の、拡張性のある、テスト可能なコンポーネントに分割する方法を考えなければなりません。 では、どうやってそれを行うのでしょうか?どのようにして「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とは、コンポーネントを自由に宣言して、他のコンポーネントからそのインスタンスを要求すれば、それが認められるということです。読み込み順とか、ファイルの位置とか、そんなことは知らなくてもいい。その威力はすぐにはわからないかもしれませんが、ひとつの(一般的な)例として、テストを挙げてみます。 例えば、私たちのアプリケーションでは、REST API を通じてサーバーサイドのストレージを実装し、アプリケーションの状態に応じてローカルのストレージも実装するサービスが必要だとします。コントローラのテストを行う際には、サーバとの通信を必要としたくありません。オリジナルのコンポーネントと同じ名前のモックサービスを追加するだけで、インジェクターはコントローラが自動的に偽のサービスを取得するようにします - コントローラは違いを知らないし、知る必要もありません。 テストといえば...

4.テスト駆動型の開発 - always (常に

これは本当はセクション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.5.概念的には、ディレクティブはパッケージ化されたjQueryではない

DOM操作はディレクティブでのみ行う」とよく聞きますが、これは必要なことです。*これは必要なことであり、敬意を持って扱わなければなりません。 しかし、もう少し深く考えてみましょう...。 いくつかのディレクティブは、ビューの中にすでにあるものを装飾するだけなので(ngClassを考えてみてください)、DOM操作をすぐに行い、基本的にはそれで完了することがあります。しかし、もしディレクティブが「ウィジェット」のようなもので、テンプレートを持っているなら、懸念事項の分離も尊重しなければなりません。つまり、テンプレートもまた、リンク関数やコントローラ関数での実装とはほとんど独立していなければなりません。 AngularJSには、これを非常に簡単に行うためのツール一式が用意されています。ngClassを使えば、クラスを動的に更新することができますし、ngModelでは双方向のデータバインディングが可能ですし、ngShowngHideでは、プログラム的に要素を表示したり隠したりすることができます。言い換えれば、DOMを操作することなく、あらゆる種類の素晴らしいことができるのです。DOM操作が少なければ少ないほど、ディレクティブのテストが容易になり、スタイリングが容易になり、将来的な変更が容易になり、再利用や配布が容易になります。 AngularJSを初めて使う開発者の多くが、jQueryを大量に投入する場所としてディレクティブを使っています。言い換えれば、「コントローラでDOM操作ができないから、そのコードをディレクティブに入れよう」と考えているのです。これは確かにはるかに良いことですが、しばしば間違っています。 セクション3でプログラムしたロガーを考えてみてください。これをディレクティブにしたとしても、「アンギュラー流」でやりたいことは変わりません。それは、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);
            });
        }
    };
});

これにはいくつかの問題があります。 1.まず、jQueryは必要ありませんでした。ここではjQueryを必要とするようなことは何もしていません。 2.angular.element "を使えば、jQueryを持たないプロジェクトに落としてもコンポーネントは動作します。 3.3. 3つ目は、仮にこのディレクティブが動作するためにjQueryが必要だったとしても、jqLite(angular.element)は、jQueryがロードされていれば、常に使用します。ですから、$を使う必要はありません。angular.elementを使えばいいのです。 4.4. 4つ目は、3つ目と密接に関連していますが、jqLiteの要素は$でラップする必要がないということです。つまり、link関数に渡されるelementは、すでにjQueryの要素になっているということです。 5.5. 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が何かをすぐにはできないとしたら、チームがどのようにしてそれを実現し、ngClickngClassなどにぴったりと合わせるかを考えるのです。

まとめ

jQueryは使わない方がいい。入れるのもやめましょう。それはあなたの足を引っ張ります。そして、jQueryで解決できると思っている問題に遭遇したら、$に手を伸ばす前に、AngularJSの中でどうやって解決するかを考えてみてください。分からなければ、聞いてみてください。20回のうち19回は、jQueryを必要としない最良の方法であり、jQueryで解決しようとすることは、結果的にあなたの仕事を増やすことになります。

解説 (22)

Imperative → declarative

jQueryでは、セレクタを使って[DOM][1]要素を見つけ、イベントハンドラをそれらにバインド/登録する。イベントが発生すると、その(命令的な)コードが実行され、DOMを更新/変更する。

AngularJSでは、DOM要素ではなくビューについて考えたいと思います。ビューは、AngularJSのディレクティブを含む(宣言的な)HTMLです。ディレクティブは、舞台裏でイベントハンドラを設定し、動的なデータバインディングを与えてくれます。セレクタはほとんど使われないので、ID(とある種のクラス)の必要性は大幅に減少します。ビューは(スコープを介して)モデルに関連付けられています。ビューはモデルを投影したものです。イベントによってモデル(つまり、データ、スコープのプロパティ)が変更されると、それらのモデルを投影したビューが "自動的に" 更新されます。

AngularJSでは、データを保持するjQueryで選択されたDOM要素ではなく、モデルについて考えます。ビューは、ユーザーが見るものを操作するためにコールバックを登録するのではなく、それらのモデルを投影するものと考えてください。

関心事の分離

jQueryはunobtrusive JavaScriptを採用しており、動作(JavaScript)は構造(HTML)から分離されています。

AngularJSはコントローラとディレクティブ(それぞれが独自のコントローラを持つことができ、またコンパイルやリンクの機能を持つ)を使用して、ビュー/構造(HTML)から動作を取り除きます。 Angularには、アプリケーションを分離/整理するのに役立つサービスフィルターもあります。

https://stackoverflow.com/a/14346528/215945 もご参照ください。

アプリケーション設計

AngularJSアプリケーションを設計するための1つのアプローチ。

1.モデルを考える。そのモデルのために、サービスや独自のJavaScriptオブジェクトを作成する。 2.2. モデルをどのように表示するか、つまりビューを考えます。動的なデータバインディングを行うために必要なディレクティブを使用して、各ビューのHTMLテンプレートを作成します。 3.3. 各ビューにコントローラを取り付けます(ng-viewとルーティング、またはng-controllerを使用)。コントローラは、ビューが仕事をするのに必要なモデルデータだけを見つけたり、取得したりします。コントローラはできるだけ薄くする。

プロトタイプ継承

JavaScriptのプロトタイピング継承の仕組みを知らなくても、jQueryで多くのことができます。AngularJSアプリケーションを開発する際、JavaScriptの継承をよく理解していれば、よくある落とし穴を避けることができます。推奨文献: https://stackoverflow.com/questions/14049480/what-are-the-nuances-of-scope-prototypal-prototypical-inheritance-in-angularjs

[1]: http://en.wikipedia.org/wiki/Document_Object_Model

解説 (7)

必要なパラダイムシフトを説明してください。

**インペラティブとデクララティブの違い

jQueryでは、DOMに必要なことを段階的に伝えます。AngularJS]1では、どのような結果が欲しいのかを記述しますが、どのようにすれば良いのかは記述しません。これについてはこちらをご覧ください。また、Mark Rajcok氏の回答もチェックしてみてください。

クライアントサイドのWebアプリを異なる方法でアーキテクト、デザインするには?

AngularJSは、MVCパターンを採用したクライアントサイドのフレームワークです(彼らの図解をご覧ください)。関心事の分離を重視しています。

最大の違いは何ですか?何をやめて、何を始めて、何を使えばいいですか?

jQueryはライブラリです。

AngularJSは美しいクライアントサイドのフレームワークで、テスト性が高く、MVC、依存性注入、データバインディングなど、多くのクールなものを組み合わせています。

関心事の分離]6とテスト(ユニットテストとエンドツーエンドテスト)に重点を置いており、テスト駆動開発を促進します。

最初に始めるのに最適な方法は、素晴らしいチュートリアルを見ることです。数時間で一通りの手順を終えることができます。しかし、舞台裏の概念を理解したい場合には、さらに読み進めるための無数の参考文献が用意されています。

サーバー側で考慮すべきことや制限事項はありますか?

すでに純粋なjQueryを使用している既存のアプリケーションに使用することができます。しかし、AngularJSの機能を十分に活用したい場合は、RESTfulアプローチを使用してサーバーサイドをコーディングすることを検討してください。

そうすることで、サーバー側のRESTfulなAPIを抽象化する「リソースファクトリー」10を活用することができ、サーバー側の呼び出し(取得、保存、削除など)が非常に簡単になります。

解説 (4)