AngularJS是Google開源出來的一款 Javascript MVC 框架。利用AngularJS,你能夠構建結構清晰、便於測試和維護的前端應用。javascript
使用AngularJS,你能夠經過directive去定義不少本身的HTML元素屬性。AngularJS無縫銜接了HTML(view)和Javascript(model),這樣你就不須要去過多地關注Dom如何變化,你只需專一的處理你的數據。html
AngularJS和服務器通訊很是方便。和大多數Javascript的MVC框架同樣, 只要你的應用提供一個RESTful API,AngularJS就能夠和你的服務器相配合。同時,AngularJS提供了基於XHR的服務,這將大大簡化你的代碼,也方便將可複用的服務抽象成API調用。前端
Raoni Boaventura提供了一個教程,一步一步地教你寫一個簡單的AngularJS應用,讓咱們一塊兒來看一下吧。java
爲了簡化問題,咱們要作的是一個直接從網絡上拉取信息的應用,好比,一個查看方程式賽車比賽信息的應用。直接用Ergast的api獲取信息。git
能夠看一下這個demo,對咱們要作一個什麼樣的應用有個大體的概念。angularjs
推薦使用augular-seed,提供了一個很好的項目骨架。github
咱們的應用的骨架大概是這樣的:web
如今要開始寫代碼了。從最重要的頁面開始吧:錦標賽的表格。chrome
HTML大概是這個樣子的(爲了提升可讀性,先忽略CSS):json
<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>
我想你會注意到模板裏面有一些{{
和 }}
之類的表達式。咱們能夠在裏面作一些計算。舉一些例子吧:
{{ 1 + 1 }} {{ 946757880 | date }} {{ user.name }}
是否是和javascript很像?不過,雖然它們很強大,咱們不該該用它們去實現一些高層級的邏輯——這該交給directive。
上面的模板裏還有一些相似ng-attributes
的語句,這些正是directive。
directive讓AngularJS把特定的行爲附加到DOM元素中。讓咱們看一下上面的模板中的例子:
ng-app
初始化你的應用,定義其做用域。在 AngularJS 中,同一頁面能夠有多個應用,ng-app
指令代表應用的首尾位置。ng-controller
定義視圖由哪一個控制器負責。在咱們的例子中,driversController
提供了車手的列表(driversList
)。ng-repeat
這個最經常使用。當使用循環的時候,ng-repeat
定義模板的範圍。在咱們的例子中,就driversList
中的每一個車手,ng-repeat
會生成重複的行。固然,沒有控制器,咱們的視圖什麼也幹不了。讓咱們在controllers.js
中添加一個driversController
:
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' ]);
這行代碼讓咱們初始化了咱們的應用,同時也登記了須要的依賴。稍後咱們會回到這個文件。
好了,如今讓咱們把這一切在index.html
中整合起來:
<!DOCTYPE html> <html> <head> <title>F-1 Feeder</title> <script src="lib/angular/angular.js"></script> <script src="lib/angular/angular-route.js"></script> <script type="text/javascript" src="js/app.js"></script> <script type="text/javascript" src="js/controllers.js"></script> <script type="text/javascript" src="js/services.js"></script> </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> </body> </html>
如今你能夠嘗試運行下這個應用了。
若是你但願調試應用,建議看下Chrome的Batarang 插件。
既然咱們已經知道如何展現這些數據了,如今該是咱們從RESTful服務器獲取信息的時候了。
AngularJS提供的$http
和$resource
幫助咱們和服務器通信。
$http
是以XMLHttpRequest和[JSONP]爲基礎的抽象層,$resource
則提供更高層的抽象。在這裏咱們使用$http
。
爲了將API調用從控制器中抽象出來,咱們建立一個本身定製的服務,該服務將抓取咱們須要的信息,將$http
封裝起來。在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' }); } return ergastAPI; });
開頭兩行,咱們建立了一個名爲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
。讓咱們把這個變量用起來。
好極了!咱們的控制器已經能夠工做了。可是它只能顯示一個車手的列表。讓咱們加一些功能吧。咱們來實現一個簡單的文本搜索框,能夠過濾列表。將如下這行加入到index.html
,加在<body>
標籤下面:
<input type="text" ng-model="nameFilter" placeholder="Search..."/>
如今咱們要用上 ng-model
指令了。這個指令將文本框綁定到$scope.nameFilter
變量,而且確保該變量的值會及時更新。如今讓咱們稍微調整下index.html
,加上一行ng-repeat
指令。
<tr ng-repeat="driver in driversList | filter: nameFilter">
這一行告訴ng-repeat
,在輸出數據以前,車手數組先要通過nameFilter
的過濾。
此刻就是雙向數據綁定發揮威力的時候了:每次你在搜索框裏鍵入一些值的時候,Angular會及時更新$scope.nameFilter
的內容。因爲綁定是雙向的,因此nameFilter
更新的時候,相應的指令(ng-repeat
)也會得到新的數值,而後視圖會馬上更新。
重啓你的應用,看看搜索框。
注意,過濾器會搜索全部屬性中的關鍵詞,包括你不想包括的內容。假設你只想經過Driver.givenName
和Driver.familyName
過濾:首先,在driversController
文件的$scope.driversList = [];
下加入這樣一行:
$scope.searchFilter = function (driver) { var keyword = new RegExp($scope.filterName, 'i'); return !$scope.filterName || keyword.test(driver.Driver.givenName) || keyword.test(driver.Driver.familyName); };
如今回到index.html
,更新包括ng-repeat
的那行:
<tr ng-repeat="driver in driversList | filter: searchFilter">
從新啓動應用,如今你能夠經過姓名搜索了。
接下來咱們要建立一個車手詳情頁面,當咱們點擊車手的時候,咱們就能夠看到關於他的一些詳細信息。
首先,咱們在app.js
里加入$routeProvider
服務,這個服務將幫助咱們處理應用路由。而後咱們加入兩個路由:一個轉向錦標賽表格,另外一個轉向車手詳情。
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
,而後在partials/drivers.html
尋找須要渲染的部分視圖。等等!咱們好像尚未部分視圖?咱們須要建立他們。
AngularJS容許你將路由綁定到特定的控制器和視圖。不過咱們首先須要告訴Angular在哪裏渲染這些部分視圖。這須要使用ng-view
指令。修改一下你的index.html
:
<!DOCTYPE html> <html> <head> <title>F-1 Feeder</title> <script src="lib/angular/angular.js"></script> <script src="lib/angular/angular-route.js"></script> <script type="text/javascript" src="js/app.js"></script> <script type="text/javascript" src="js/controllers.js"></script> <script type="text/javascript" src="js/services.js"></script> </head> <body ng-app="F1FeederApp"> <ng-view></ng-view> </body> </html>
如今,只要是經過應用路由瀏覽,Angular 就會加載相應的視圖,而且
在<ng-view>
標籤處渲染。你所須要作的只是建立一個名爲partials/drivers.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.filterName, 'i'); return !$scope.filterName || 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 id="main"> <nav id="secondary" 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}}<br/>{{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
的時候,它纔會顯示HTML元素。在咱們的例子中,只有當控制器加載了車手對象後纔會顯示頭像。
加上一些CSS以後,大體是這樣的效果:
如今你的應用已經能夠上線了。確認下路由能工做。你能夠在index.html
加入一個靜態的菜單以增強導航。一切皆有可能。
咱們已經介紹了開發一個簡單應用所需的一切。最後別忘了,Angular是一個很是強大的框架。咱們只是試了試水而已。之後有機會將向你們展現Angular區別於其餘前端MVC框架的特性:可測試性。咱們將評測使用Karma編寫和運行測試的過程,介紹持續集成的工具Yeomen、Grunt和Bower,以及Angular的其餘優點。敬請期待。
原文 A Step-by-Step Guide to Your First AngularJS App
編譯 SegmentFault