天天看點

你的第一個AngularJS應用

AngularJS是Google開源出來的一款 Javascript MVC 架構。利用AngularJS,你可以建構結構清晰、便于測試和維護的前端應用。

使用AngularJS,你可以通過directive去定義很多自己的HTML元素屬性。AngularJS無縫銜接了HTML(view)和Javascript(model),這樣你就不需要去過多地關注Dom如何變化,你隻需專注的處理你的資料。

AngularJS和伺服器通信非常友善。和大多數Javascript的MVC架構一樣, 隻要你的應用提供一個RESTful API,AngularJS就可以和你的伺服器相配合。同時,AngularJS提供了基于XHR的服務,這将大大簡化你的代碼,也友善将可複用的服務抽象成API調用。

Raoni Boaventura

提供了一個教程,一步一步地教你寫一個簡單的AngularJS應用,讓我們一起來看一下吧。

為了簡化問題,我們要做的是一個直接從網絡上拉取資訊的應用,比如,一個檢視方程式賽車比賽資訊的應用。直接用

Ergast

的api擷取資訊。

可以看一下這個

demo ,對我們要做一個什麼樣的應用有個大緻的概念。

開動吧

推薦使用

augular-seed

,提供了一個很好的項目骨架。

我們的應用的骨架大概是這樣的:

你的第一個AngularJS應用

現在要開始寫代碼了。從最重要的頁面開始吧:錦标賽的表格。

你的第一個AngularJS應用

HTML大概是這個樣子的(為了提高可讀性,先忽略CSS):

<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}}&nbsp;{{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。

了解基本的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"}

          ]

      },

          givenName: 'Fernando',

              familyName: 'Alonso'

          points: 207,

          nationality: "Spanish",

              {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>

</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

注冊下即可:

  'F1FeederApp.controllers',

  'F1FeederApp.services'

現在我們隻需調整一下

controller.js

,将

ergastAPIservice

作為依賴:

  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

)也會獲得新的數值,然後視圖會立刻更新。

重新開機你的應用,看看搜尋框。

你的第一個AngularJS應用

注意,過濾器會搜尋所有屬性中的關鍵詞,包括你不想包括的内容。假設你隻想通過

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

服務,這個服務将幫助我們處理應用路由。然後我們加入兩個路由:一個轉向錦标賽表格,另一個轉向車手詳情。

  'F1FeederApp.services',

  '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

<body ng-app="F1FeederApp">

  <ng-view></ng-view>

現在,隻要是通過應用路由浏覽,Angular 就會加載相應的視圖,并且

<ng-view>

标簽處渲染。你所需要做的隻是建立一個名為

partials/drivers.html

的檔案,然後将錦标賽表格放在那裡。同時我們也将将車手的姓名和詳情頁面連接配接起來。

<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}}&nbsp;{{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) {

    ergastAPI.getDriverDetails = function(id) {

        url: 'http://ergast.com/api/f1/2013/drivers/'+ id +'/driverStandings.json?callback=JSON_CALLBACK'

    ergastAPI.getDriverRaces = function(id) {

        url: 'http://ergast.com/api/f1/2013/drivers/'+ id +'/results.json?callback=JSON_CALLBACK'

這次我們把車手的ID提供給服務,這樣我們就可以擷取特定車手的資訊了。修改一下

controllers.js

  /* Drivers controller */

    $scope.searchFilter = function (driver) {

        var re = new RegExp($scope.filterName, 'i');

        return !$scope.filterName || re.test(driver.Driver.givenName) || re.test(driver.Driver.familyName);

    };

        //Digging into the response to get the relevant data

  }).

  /* 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>

  </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>

      </tbody>

    </table>

  </div>

</section>

注意這次我們用上了

ng-show

。隻有當你提供的表達式是

true

的時候,它才會顯示HTML元素。在我們的例子中,隻有當控制器加載了車手對象後才會顯示頭像。

完成

加上一些CSS之後,大緻是這樣的效果:

你的第一個AngularJS應用

現在你的應用已經可以上線了。确認下路由能工作。你可以在

index.html

加入一個靜态的菜單以加強導航。一切皆有可能。

結論

我們已經介紹了開發一個簡單應用所需的一切。最後别忘了,Angular是一個非常強大的架構。我們隻是試了試水而已。以後有機會将向大家展示Angular差別于其他前端MVC架構的特性:可測試性。我們将評測使用

Karma

編寫和運作測試的過程,介紹持續內建的工具

Yeomen

Grunt Bower

,以及Angular的其他優勢。敬請期待。

繼續閱讀