最初のAngularJSアプリのステップバイステップのチュートリアル
公開: 2022-03-11AngularJSとは何ですか?
AngularJSは、Googleによって開発されたJavaScript MVCフレームワークであり、適切に構造化され、テストが容易で、保守が容易なフロントエンドアプリケーションを構築できます。
そして、なぜ私はそれを使うべきですか?
AngularJSをまだ試したことがない場合は、見逃していることになります。 フレームワークは、緊密に統合されたツールセットで構成されており、コードを減らして柔軟性を高めながら、モジュール方式で適切に構造化されたリッチクライアント側アプリケーションを構築するのに役立ちます。
AngularJSは、マークアップに機能を追加し、強力な動的テンプレートを作成できるようにするディレクティブを提供することでHTMLを拡張します。 独自のディレクティブを作成して、ニーズを満たす再利用可能なコンポーネントを作成し、すべてのDOM操作ロジックを抽象化することもできます。
また、双方向のデータバインディングを実装し、HTML(ビュー)をJavaScriptオブジェクト(モデル)にシームレスに接続します。 簡単に言うと、これは、モデルの更新がDOM操作やイベント処理(jQueryなど)を必要とせずに、すぐにビューに反映されることを意味します。
最後に、サーバー通信に関する柔軟性があるAngularが大好きです。 ほとんどのJavaScriptMVCフレームワークと同様に、RESTful Web APIを介してアプリを提供できる限り、サーバー側のテクノロジーを使用できます。 ただし、AngularはXHRに加えて、コードを劇的に簡素化し、API呼び出しを再利用可能なサービスに抽象化できるサービスも提供します。 その結果、モデルとビジネスロジックをフロントエンドに移動し、バックエンドに依存しないWebアプリを構築できます。 この投稿では、それを1ステップずつ実行します。
では、どこから始めればよいのでしょうか。
まず、作成するアプリの性質を決定しましょう。 このガイドでは、バックエンドにあまり時間をかけたくないので、スポーツフィードアプリのように、インターネットで簡単に取得できるデータに基づいて何かを記述します。
私はたまたまモーターレースとフォーミュラ1の大ファンなので、オートスポーツAPIサービスを使用してバックエンドとして機能します。 幸いなことに、Ergastのスタッフは、私たちにぴったりの無料のモータースポーツAPIを提供してくれます。
構築するもののスニークピークについては、ライブデモをご覧ください。 デモを美しく見せるために、Angularテンプレートをいくつか紹介するために、WrapBootstrapのBootstrapテーマを適用しましたが、この記事はCSSに関するものではないため、例から抽象化して省略します。
入門チュートリアル
ボイラープレートを使用して、サンプルアプリをキックスタートしましょう。 ブートストラップ用の優れたスケルトンを提供するだけでなく、KarmaとJasmineを使用した単体テストの基礎を確立するため、Angular-Seedプロジェクトをお勧めします(このデモではテストを行わないため、とりあえずそのようなものは脇に置いておきます。ユニットテストとエンドツーエンドテスト用にプロジェクトを設定する方法の詳細については、このチュートリアルのパート2を参照してください)。
編集(2014年5月):このチュートリアルを書いた後、angular-seedプロジェクトはいくつかの大きな変更を経ました(パッケージマネージャーとしてのBowerの追加を含む)。 プロジェクトの展開方法について疑問がある場合は、リファレンスガイドの最初のセクションをざっと見てください。 チュートリアルのパート2では、他のツールの中でも特にBowerについて詳しく説明します。
OK、リポジトリのクローンを作成して依存関係をインストールしたので、アプリのスケルトンは次のようになります。
これでコーディングを開始できます。 レーシングチャンピオンシップのスポーツフィードを作成しようとしているので、最も関連性の高いビューであるチャンピオンシップテーブルから始めましょう。
スコープ内にドライバーリストがすでに定義されており(私と一緒に-そこに着きます)、CSSを無視すると(読みやすくするため)、HTMLは次のようになります。
<body ng-app="F1FeederApp" ng-controller="driversController"> <table> <thead> <tr><th colspan="4">Drivers Championship Standings</th></tr> </thead> <tbody> <tr ng-repeat="driver in driversList"> <td>{{$index + 1}}</td> <td> <img src="img/flags/{{driver.Driver.nationality}}.png" /> {{driver.Driver.givenName}} {{driver.Driver.familyName}} </td> <td>{{driver.Constructors[0].name}}</td> <td>{{driver.points}}</td> </tr> </tbody> </table> </body>
このテンプレートで最初に気付くのは、式(“ {{“ and“}}”)を使用して変数値を返すことです。 AngularJS開発では、式を使用すると、目的の値を返すためにいくつかの計算を実行できます。 有効な式は次のとおりです。
-
{{ 1 + 1 }}
-
{{ 946757880 | date }}
-
{{ user.name }}
事実上、式はJavaScriptのようなスニペットです。 ただし、非常に強力であるにもかかわらず、式を使用して高レベルのロジックを実装するべきではありません。 そのために、ディレクティブを使用します。
基本的なディレクティブを理解する
次に気付くのは、通常のマークアップでは見られないng-attributes
の存在です。 それらはディレクティブです。
大まかに言うと、ディレクティブはマーカー(属性、タグ、クラス名など)であり、AngularJSに特定の動作をDOM要素にアタッチする(または変換する、置き換えるなど)ように指示します。 すでに見たものを見てみましょう:
ng-app
ディレクティブは、スコープを定義するアプリのブートストラップを担当します。 AngularJSでは、同じページ内に複数のアプリを含めることができるため、このディレクティブは、それぞれの個別のアプリの開始位置と終了位置を定義します。ng-controller
ディレクティブは、ビューを担当するコントローラーを定義します。 この場合、ドライバーのリスト(driversList
)を提供するdriversController
を示します。ng-repeat
ディレクティブは、最も一般的に使用されるものの1つであり、コレクションをループするときにテンプレートスコープを定義するのに役立ちます。 上記の例では、driversList
内の各ドライバーのテーブル内の行を複製します。
コントローラーの追加
もちろん、コントローラーなしではビューを使用することはできません。 driversController
をcontrollers.jsに追加しましょう:
angular.module('F1FeederApp.controllers', []). controller('driversController', function($scope) { $scope.driversList = [ { Driver: { givenName: 'Sebastian', familyName: 'Vettel' }, points: 322, nationality: "German", Constructors: [ {name: "Red Bull"} ] }, { Driver: { givenName: 'Fernando', familyName: 'Alonso' }, points: 207, nationality: "Spanish", Constructors: [ {name: "Ferrari"} ] } ]; });
コントローラにパラメータとして渡す$scope
変数に気付いたかもしれません。 $scope
変数は、コントローラーとビューをリンクすることになっています。 特に、テンプレート内で使用されるすべてのデータを保持します。 追加したもの(上記の例のdriversList
など)は、ビューから直接アクセスできます。 今のところ、ダミー(静的)データ配列を操作してみましょう。これは後でAPIサービスに置き換えます。
次に、これをapp.jsに追加します。
angular.module('F1FeederApp', [ 'F1FeederApp.controllers' ]);
このコード行を使用して、実際にアプリを初期化し、依存するモジュールを登録します。 後でそのファイル( app.js
)に戻ります。
それでは、すべてをindex.html
にまとめましょう。
<!DOCTYPE html> <html> <head> <title>F-1 Feeder</title> </head> <body ng-app="F1FeederApp" ng-controller="driversController"> <table> <thead> <tr><th colspan="4">Drivers Championship Standings</th></tr> </thead> <tbody> <tr ng-repeat="driver in driversList"> <td>{{$index + 1}}</td> <td> <img src="img/flags/{{driver.Driver.nationality}}.png" /> {{driver.Driver.givenName}} {{driver.Driver.familyName}} </td> <td>{{driver.Constructors[0].name}}</td> <td>{{driver.points}}</td> </tr> </tbody> </table> <script src="bower_components/angular/angular.js"></script> <script src="bower_components/angular-route/angular-route.js"></script> <script src="js/app.js"></script> <script src="js/services.js"></script> <script src="js/controllers.js"></script> </body> </html>
Moduloの小さな間違い、アプリを起動して(静的な)ドライバーのリストを確認できるようになりました。
注:アプリのデバッグや、ブラウザー内でのモデルとスコープの視覚化についてサポートが必要な場合は、Chrome用のすばらしいBatarangプラグインを確認することをお勧めします。
サーバーからのデータのロード
コントローラーのデータをビューに表示する方法はすでにわかっているので、RESTfulサーバーから実際にライブデータをフェッチするときが来ました。
HTTPサーバーとの通信を容易にするために、AngularJSは$http
および$resource
サービスを提供します。 前者はXMLHttpRequestまたはJSONPの上のレイヤーにすぎませんが、後者はより高いレベルの抽象化を提供します。 $http
を使用します。
コントローラーからサーバーAPI呼び出しを抽象化するために、データをフェッチし、これをservices.js
に追加することで、 $http
のラッパーとして機能する独自のカスタムサービスを作成しましょう。
angular.module('F1FeederApp.services', []). factory('ergastAPIservice', function($http) { var ergastAPI = {}; ergastAPI.getDrivers = function() { return $http({ method: 'JSONP', url: 'http://ergast.com/api/f1/2013/driverStandings.json?callback=JSON_CALLBACK' }); } return ergastAPI; });
最初の2行で、新しいモジュール( F1FeederApp.services
)を作成し、そのモジュール( ergastAPIservice
)内にサービスを登録します。 そのサービスへのパラメーターとして$http
を渡すことに注意してください。 これは、Angularの依存性注入エンジンに、新しいサービスが$http
サービスを必要とする(または依存する)ことを通知します。
同様の方法で、新しいモジュールをアプリに含めるようにAngularに指示する必要があります。 それをapp.js
に登録して、既存のコードを次のように置き換えましょう。
angular.module('F1FeederApp', [ 'F1FeederApp.controllers', 'F1FeederApp.services' ]);
これで、 controller.js
を少し調整し、依存関係としてergastAPIservice
を含めるだけで、次のことができます。
angular.module('F1FeederApp.controllers', []). controller('driversController', function($scope, ergastAPIservice) { $scope.nameFilter = null; $scope.driversList = []; ergastAPIservice.getDrivers().success(function (response) { //Dig into the responde to get the relevant data $scope.driversList = response.MRData.StandingsTable.StandingsLists[0].DriverStandings; }); });
次に、アプリをリロードして結果を確認します。 テンプレートに変更を加えていないことに注意してください。ただし、スコープにnameFilter
変数を追加しました。 その変数を使用してみましょう。
フィルタ
すごい! 機能的なコントローラーがあります。 ただし、ドライバーのリストのみが表示されます。 リストをフィルタリングする簡単なテキスト検索入力を実装して、いくつかの機能を追加しましょう。 <body>
タグのすぐ下のindex.html
に次の行を追加しましょう。
<input type="text" ng-model="nameFilter" placeholder="Search..."/>
現在、 ng-model
ディレクティブを使用しています。 このディレクティブは、テキストフィールドを$scope.nameFilter
変数にバインドし、その値が入力値で常に最新であることを確認します。 それでは、もう一度index.htmlにアクセスして、 ng-repeat
ディレクティブを含む行を少し調整してみましょう。
<tr ng-repeat="driver in driversList | filter: nameFilter">
この行は、データを出力する前に、 driversList
配列をnameFilter
に格納されている値でフィルタリングする必要があることをng-repeat
に通知します。

この時点で、双方向のデータバインディングが開始されます。検索フィールドに値が入力されるたびに、Angularは、関連付けられた$scope.nameFilter
が新しい値で更新されることをすぐに確認します。 バインディングは双方向で機能するため、 nameFilter
値が更新されると、それに関連付けられている2番目のディレクティブ(つまり、 ng-repeat
)も新しい値を取得し、ビューがすぐに更新されます。
アプリをリロードして、検索バーを確認してください。
このフィルターは、使用されていなかったものも含め、モデルのすべての属性でキーワードを検索することに注意してください。 Driver.givenName
とDriver.familyName
でのみフィルタリングしたいとします。まず、 $scope.driversList = [];
のすぐ下にあるdriversController
に追加します。 ライン:
$scope.searchFilter = function (driver) { var keyword = new RegExp($scope.nameFilter, 'i'); return !$scope.nameFilter || keyword.test(driver.Driver.givenName) || keyword.test(driver.Driver.familyName); };
ここで、 index.html
に戻り、 ng-repeat
ディレクティブを含む行を更新します。
<tr ng-repeat="driver in driversList | filter: searchFilter">
アプリをもう一度リロードすると、名前で検索できるようになります。
ルート
次の目標は、各ドライバーをクリックしてそのキャリアの詳細を確認できるドライバー詳細ページを作成することです。
まず、これらのさまざまなアプリケーションルートを処理するのに役立つ$routeProvider
サービス( app.js
)を含めましょう。 次に、そのようなルートを2つ追加します。1つはチャンピオンシップテーブル用で、もう1つはドライバーの詳細用です。 これが私たちの新しいapp.js
です:
angular.module('F1FeederApp', [ 'F1FeederApp.services', 'F1FeederApp.controllers', 'ngRoute' ]). config(['$routeProvider', function($routeProvider) { $routeProvider. when("/drivers", {templateUrl: "partials/drivers.html", controller: "driversController"}). when("/drivers/:id", {templateUrl: "partials/driver.html", controller: "driverController"}). otherwise({redirectTo: '/drivers'}); }]);
この変更により、 http://domain/#/drivers
に移動すると、driversControllerが読み込まれ、 driversController
partials/drivers.html
drivers.htmlでレンダリングする部分ビューが検索されます。 ちょっと待って! まだ部分的な見方はありませんよね? それらも作成する必要があります。
部分ビュー
AngularJSを使用すると、ルートを特定のコントローラーとビューにバインドできます。
ただし、最初に、これらの部分ビューをレンダリングする場所をAngularに指示する必要があります。 そのために、 ng-view
ディレクティブを使用して、以下をミラーリングするようにindex.html
を変更します。
<!DOCTYPE html> <html> <head> <title>F-1 Feeder</title> </head> <body ng-app="F1FeederApp"> <ng-view></ng-view> <script src="bower_components/angular/angular.js"></script> <script src="bower_components/angular-route/angular-route.js"></script> <script src="js/app.js"></script> <script src="js/services.js"></script> <script src="js/controllers.js"></script> </body> </html>
これで、アプリルートをナビゲートするたびに、Angularは関連するビューを読み込み、 <ng-view>
タグの代わりにレンダリングします。 必要なのは、 partials/drivers.html
という名前のファイルを作成し、そこにチャンピオンシップテーブルのHTMLを配置することだけです。 また、この機会を利用して、ドライバー名をドライバー詳細ルートにリンクします。
<input type="text" ng-model="nameFilter" placeholder="Search..."/> <table> <thead> <tr><th colspan="4">Drivers Championship Standings</th></tr> </thead> <tbody> <tr ng-repeat="driver in driversList | filter: searchFilter"> <td>{{$index + 1}}</td> <td> <img src="img/flags/{{driver.Driver.nationality}}.png" /> <a href="#/drivers/{{driver.Driver.driverId}}"> {{driver.Driver.givenName}} {{driver.Driver.familyName}} </a> </td> <td>{{driver.Constructors[0].name}}</td> <td>{{driver.points}}</td> </tr> </tbody> </table>
最後に、詳細ページに何を表示するかを決めましょう。 ドライバーに関連するすべての事実(出生、国籍など)の要約と、最近の結果を含む表はどうですか? そのために、 services.js
に追加します。
angular.module('F1FeederApp.services', []) .factory('ergastAPIservice', function($http) { var ergastAPI = {}; ergastAPI.getDrivers = function() { return $http({ method: 'JSONP', url: 'http://ergast.com/api/f1/2013/driverStandings.json?callback=JSON_CALLBACK' }); } ergastAPI.getDriverDetails = function(id) { return $http({ method: 'JSONP', url: 'http://ergast.com/api/f1/2013/drivers/'+ id +'/driverStandings.json?callback=JSON_CALLBACK' }); } ergastAPI.getDriverRaces = function(id) { return $http({ method: 'JSONP', url: 'http://ergast.com/api/f1/2013/drivers/'+ id +'/results.json?callback=JSON_CALLBACK' }); } return ergastAPI; });
今回は、特定のドライバーのみに関連する情報を取得できるように、ドライバーのIDをサービスに提供します。 ここで、 controllers.js
を変更します。
angular.module('F1FeederApp.controllers', []). /* Drivers controller */ controller('driversController', function($scope, ergastAPIservice) { $scope.nameFilter = null; $scope.driversList = []; $scope.searchFilter = function (driver) { var re = new RegExp($scope.nameFilter, 'i'); return !$scope.nameFilter || re.test(driver.Driver.givenName) || re.test(driver.Driver.familyName); }; ergastAPIservice.getDrivers().success(function (response) { //Digging into the response to get the relevant data $scope.driversList = response.MRData.StandingsTable.StandingsLists[0].DriverStandings; }); }). /* Driver controller */ controller('driverController', function($scope, $routeParams, ergastAPIservice) { $scope.id = $routeParams.id; $scope.races = []; $scope.driver = null; ergastAPIservice.getDriverDetails($scope.id).success(function (response) { $scope.driver = response.MRData.StandingsTable.StandingsLists[0].DriverStandings[0]; }); ergastAPIservice.getDriverRaces($scope.id).success(function (response) { $scope.races = response.MRData.RaceTable.Races; }); });
ここで注意すべき重要なことは、 $routeParams
サービスをドライバーコントローラーに挿入したことです。 このサービスにより、 $routeParams.id
を使用してURLパラメーター(この場合は:id
)にアクセスできるようになります。
スコープにデータが含まれるようになったので、残りの部分ビューのみが必要です。 partials/driver.html
という名前のファイルを作成し、以下を追加しましょう。
<section> <a href="./#/drivers"><- Back to drivers list</a> <nav class="main-nav"> <div class="driver-picture"> <div class="avatar"> <img ng-show="driver" src="img/drivers/{{driver.Driver.driverId}}.png" /> <img ng-show="driver" src="img/flags/{{driver.Driver.nationality}}.png" /><br/> {{driver.Driver.givenName}} {{driver.Driver.familyName}} </div> </div> <div class="driver-status"> Country: {{driver.Driver.nationality}} <br/> Team: {{driver.Constructors[0].name}}<br/> Birth: {{driver.Driver.dateOfBirth}}<br/> <a href="{{driver.Driver.url}}" target="_blank">Biography</a> </div> </nav> <div class="main-content"> <table class="result-table"> <thead> <tr><th colspan="5">Formula 1 2013 Results</th></tr> </thead> <tbody> <tr> <td>Round</td> <td>Grand Prix</td> <td>Team</td> <td>Grid</td> <td>Race</td> </tr> <tr ng-repeat="race in races"> <td>{{race.round}}</td> <td><img src="img/flags/{{race.Circuit.Location.country}}.png" />{{race.raceName}}</td> <td>{{race.Results[0].Constructor.name}}</td> <td>{{race.Results[0].grid}}</td> <td>{{race.Results[0].position}}</td> </tr> </tbody> </table> </div> </section>
現在、 ng-show
ディレクティブを有効に活用していることに注意してください。 このディレクティブは、提供された式がtrue
(つまり、 false
でもnull
でもない)の場合にのみHTML要素を表示します。 この場合、アバターは、コントローラーによってドライバーオブジェクトがスコープにロードされた後にのみ表示されます。
仕上げ
たくさんのCSSを追加して、ページをレンダリングします。 最終的には次のようになります。
これで、アプリを起動して、両方のルートが希望どおりに機能していることを確認する準備が整いました。 また、静的メニューをindex.html
に追加して、ユーザーのナビゲーション機能を向上させることもできます。 可能性は無限大。
編集(2014年5月):このチュートリアルで作成したコードのダウンロード可能なバージョンに対する多くのリクエストを受け取りました。 したがって、ここでリリースすることにしました(CSSを削除しました)。 ただし、このガイドには、同じアプリケーションを自分の手で作成するために必要なすべてのステップが含まれているため、ダウンロードすることはお勧めしません。これは、はるかに便利で効果的な学習演習になります。
結論
チュートリアルのこの時点で、単純なアプリ(F1フィーダーなど)を作成するために必要なすべてをカバーしました。 ライブデモの残りの各ページ(コンストラクターチャンピオンシップテーブル、チームの詳細、カレンダーなど)は、ここで確認したものと同じ基本構造と概念を共有しています。
最後に、Angularは非常に強力なフレームワークであり、提供するすべての点で表面をほとんど傷つけていないことを忘れないでください。 このチュートリアルのパート2では、AngularがピアフロントエンドMVCフレームワークの中で際立っている理由の例を示します:テスト容易性。 Karmaを使用して単体テストを作成および実行し、Yeomen、Grunt、Bowerとの継続的インテグレーションを実現するプロセス、およびこの素晴らしいフロントエンドフレームワークの他の長所を確認します。