天天看點

七步從AngularJS菜鳥到專家(4和5):指令和表達式

本文由  伯樂線上  -  兔米醬  翻譯自  ng-newsletter 。歡迎加入 技術翻譯小組 。轉載請參見文章末尾處的要求。

這一篇包含了"AngularJS - 七步從菜鳥到專家"系列的第四篇(指令)和第五篇(表達式)。

之前的幾篇展示了我們應用的核心元件,以及如何設定搭建一個Angular.js應用。在這一部分,我們會厘清一些術語,然後深入探讨很多Angular.js提供的核心功能。

通過這整個系列的教程,我們會開發一個NPR(美國全國公共廣播電台)廣播的音頻播放器,它能顯示Morning Edition節目裡現在播出的最新故事,并在我們的浏覽器裡播放它們。完成版的Demo可以看這裡(http://www.ng-newsletter.com/code/beginner_series/)

第四部分 指令屬性

目前為止,我們已提到過幾次“指令屬性”的概念,但從未深入探讨過它到底是什麼。實際上,“指令屬性”就是綁定在DOM元素上的函數,它可以調用方法、定義行為、綁定controller及$scope對象、操作DOM,等等等等。

當浏覽器啟動、開始解析HTML(像平時一樣)時,DOM元素上的指令屬性就會跟其他屬性一樣被解析。

當一個Angular.js應用啟動,Angular編譯器就會周遊DOM樹(從有ng-app指令屬性的那個DOM元素開始,如我們在本系列第一篇裡所提過的),解析HTML,尋找這些指令屬性函數。

當在一個DOM元素上找到一個或多個這樣的指令屬性函數,它們就會被收集起來、排序,然後按照優先級順序被執行。

每個指令屬性都有自己的優先級,在我們關于指令屬性的專題文章裡(http://www.ng-newsletter.com/posts/directives.html),你可以找到更深入的資訊。

Angular.js應用的動态性和響應能力,都要歸功于指令屬性。之前我們已經看過一些指令屬性的用例:

ng-model

1 2

<

input

ng-model

=

"name"

name

=

"Name"

placeholder

=

"Enter your name"

/>

<

h4

>Your name: {{ name }}</

h4

>

試試看 

七步從AngularJS菜鳥到專家(4和5):指令和表達式

ng-model指令屬性(我們在之前的章節使用過它),被用來将DOM文本輸入框的值,跟controller裡的$scope model綁定起來。具體的實作過程,是在這個值上綁定了一個$watch函數(類似JavaScript裡的事件監聽函數)。

$watch函數(在使用時)運作在Angular.js的事件循環(即$digest循環)裡,讓Angular.js能夠對DOM進行相應的更新。請關注我們關于$digest循環的進階文章!

在Angular.js應用的開發中,我們用指令屬性來将行為綁定到DOM上。指令屬性的使用,是一個 應用能否擁有動态性、響應能力的關鍵。Angular.js提供了很多預設的指令屬性,現在讓我們來看看其中幾個,以及如何使用它們。

幾個常見的指令屬性

{{ 表達式 }}

這個雙大括号指令屬性,使用$watch()函數,給括号内的表達式注冊了一個監聽器。正是這個$watch函數,讓Angular.js能夠實時自動更新view。

那麼,到底什麼算是個表達式?

第五部分 表達式

要想了解指令屬性的運作,我們必須先了解表達式,是以這裡我們就繞個路,看看本系列的第五部分——表達式。在之前的例子裡我們已經見過表達式,例如 {{ person.name }} 和 {{ clock }}。

1 2 3

{{

8

+

1

}}

9

{{ person }}{

"name"

:

"Ari Lerner"

}

{{

10

*

3.3

| currency }}$

33.00

最後的例子裡 (10 * 3.3 | currency) 用了一個過濾器。本系列之後的部分,會深入介紹過濾器。

表達式粗略來看有點像 eval(javascript) 的結果。它們會經過Angular.js的處理,進而擁有以下重要而獨特的性質:

  • 所有表達式都在scope這個context裡被執行,是以可以使用所有本地 $scope 中的變量。
  • 如果一個表達式的執行導緻類型錯誤或引用錯誤,這些錯誤将不會被抛出。
  • 表達式裡不允許任何控制函數流程的功能(如if/else等條件語句)
  • 表達式可接受一個或多個串聯起來的過濾器。

試試看 

七步從AngularJS菜鳥到專家(4和5):指令和表達式

試試輸入“person“,“clock“或其他數學算式如2+4。你甚至可以操作scope,例如,試試輸入person.name = ”Ari”; person.age = 28; person 或 clock

表達式都運作在調用它們的scope裡,是以一個表達式可通路并操作其scope上的一切。由此,你可以使用表達式周遊其scope的屬性(我們在ng-repeat中會看到這一應用)、調用scope裡的函數,或者對scope中的變量進行數學運算。

現在,讓我們回到指令屬性…

ng-init

ng-init指令屬性是一個在啟動時運作的函數(在程式進入運作階段之前)。它讓我們能夠在程式運作前設定初始變量的值:

1

<b ng-init=

'name = "Ari Lerner"'

>Hello, {{ name }}</b>

試試看 

七步從AngularJS菜鳥到專家(4和5):指令和表達式

ng-click

ng-click指令屬性給DOM元素注冊了一個點選事件的監聽器。當此DOM元素上有點選事件發生(即當此button或link被點選時),Angular.js就會執行表達式的内容,并相應地更新view。

1 2

<button ng-click=

"counter = counter + 1"

>Add one</button>

Current counter: {{ counter }}

試試看

七步從AngularJS菜鳥到專家(4和5):指令和表達式

我們也可以用ng-click 來調用在controller裡寫好并綁定在$scope上的函數,例如:

1

<button ng-click=

"sayHello()"

>Say hello</button>

controller 裡的函數:

1 2 3 4 5

app.controller(

'MyController'

,

function

($scope) {

$scope.sayHello =

function

() {

alert(

"hello!"

);

}

});

試試看

七步從AngularJS菜鳥到專家(4和5):指令和表達式

ng-show / ng-hide

The ng-show and ng-hide directives show or hide a portion of the DOM depending on whether the expression is truthy.

ng-show和ng-hide指令,根據賦予它們的表達式的值的真假性(truthy),來顯示和隐藏它們所屬的那一部分DOM。

我們在這裡不會深入,但你應該熟悉JavaScript中變量值的“truthy”和“falsy”概念。

1 2 3 4 5 6

<button ng-init=

"shouldShow = true"

ng-click=

"shouldShow = !shouldShow"

>Flip the shouldShow variable</button>

<div ng-show=

"shouldShow"

>

<h3>Showing {{ shouldShow }}</h3>

</div> <div ng-hide=

"shouldShow"

>

<h3>Hiding {{ shouldShow }}</h3>

</div>

試試看

圖6

ng-repeat

ng-repeat指令周遊一個資料集合中的每個資料元素,加載HTML模版把資料渲染出來。被重複使用的模版元素,就是我們綁定了這個指令屬性的DOM元素。每一個使用模版渲染的DOM元素都有自己的scope。

在更多的解釋之前,我們先看一個例子。假設我們的controller裡有這樣一個資料元素的數組:

1 2 3 4 5 6

$scope.roommates = [

{ name:

'Ari'

},

{ name:

'Q'

},

{ name:

'Sean'

},

{ name:

'Anand'

}

];

在view裡我們可以用ng-repeat來周遊這個集合裡的資料:

1 2

<ul>

<li ng-repeat=

"person in roommates"

>{{ person.name }}</li> </ul>

請看

  • Ari
  • Q
  • Sean
  • Anand

對賦予ng-repeat的表達式稍作改動,我們還可以用它周遊一個由成對的key-value資料組成的集合。例如,假設我們有一個人名和他們最喜歡的顔色的資料集合:

1 2 3 4 5

$scope.people = {

'Ari'

:

'orange'

,

'Q'

:

'purple'

,

'Sean'

:

'green'

}

要周遊它,我們可以給ng-repeat指令屬性賦予這個表達式: (key, value) in object:

1 2 3 4

<ul>

<li ng-repeat=

"(name, color) in people"

>{{ name }}'s favorite color is {{ color }}

</li>

</ul>

請看

  • Ari’s favorite color is orange
  • Q’s favorite color is blue
  • Sean’s favorite color is green

Angular.js提供的直接可用的指令屬性并不多,但它讓我們可以很容易地建立自己的指令屬性。請到這裡檢視我們的指令屬性建立指南:http://www.ng-newsletter.com/posts/directives.html

我們應用中的指令屬性

在上一篇中,我們的收音機應用隻從NPR API取回了最新的音頻節目清單:

1

$scope.programs = data.list.story;

現在我們學了周遊一個list的實作方法,可以在我們的收音機應用裡,像剛才那樣用ng-repeat來周遊這個節目清單了:

1 2 3 4 5

<ul id=

"programs_list"

class=

""

>

<li ng-repeat=

"program in programs"

>

<span class=

"large-12"

>{{ program.title.$text }}</span>

</li>

</ul>

NPR API給我們的是一個有title+$text的清單,這個結構是NPR API所特有的,而不是Angular.js的。

現在我們列出了節目和它們的标題,但還不能點選并播放它們。用ng-click我們可以給HTML元素加上一個點選功能:

1 2 3 4 5

<ul id=

"programs_list"

class=

""

>

<li ng-repeat=

"program in programs"

ng-click=

"play(program)"

>

<span class=

"large-12"

>{{ program.title.$text }}</span>

</li>

</ul>

通過這一步,我們把一個play動作函數綁定到了清單裡的<li>DOM元素上。現在,我們在PlayerController裡建立這個play動作函數,然後我們就有了一個功能完備的音頻應用:

1 2 3 4 5 6 7 8

// format.mp4.$text是NPR API給我們的到這個音頻mp4檔案的路徑 $scope.play = function(program) {

if

($scope.playing) audio.pause();

var

url = program.audio[0].format.mp4.$text;

audio.src = url;

audio.play();

// 儲存播放器的狀态為正在播放

$scope.playing =

true

;

}

現在這個應用功能完備了,但是還不太好看。而且随着我們繼續添加新功能,代碼也會膨脹,變得難以管理。我們可以建立自己的指令屬性,來幫助我們減少複雜性。

想更多地學習自定義指令屬性,可以看看我們深入探讨指令屬性的文章:http://www.ng-newsletter.com/posts/directives.html

建立自定義指令屬性,我們使用app對象的directive方法:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

app.directive(

'nprLink'

,

function

() {

return

{

restrict:

'EA'

,

require: [

'^ngModel'

],

replace

:

true

,

scope: {

ngModel:

'='

,

play:

'&'

},

templateUrl:

'/views/nprListItem.html'

,

link:

function

(scope, ele, attr) {

scope.duration = scope.ngModel.audio[

].duration.$text;

}

}

});

我們不會逐個解釋每個選項的意義,因為我們有一篇專門的深入文章來介紹它們(http://www.ng-newsletter.com/posts/directives.html)。這裡我們隻需要明白,現在我們就能在HTML裡使用這個自定義的指令屬性了,它會将它所在的DOM元素替換為我們給定的templateUrl所指向的模版裡的内容(在 /views/nprListItem 中)。

現在,我們的主HTML檔案可以保持整潔,而将用來渲染清單内容的view,建立在這個單獨提取出來的模版檔案裡:

1 2 3 4 5 6 7 8 9 10 11 12 13 14

<div

class

=

"nprLink row"

ng-click=

"play(ngModel)"

>

<span

class

=

"name large-8 columns"

>

<button

class

=

"large-2 small-2 playButton columns"

><div

class

=

"triangle"

></div></button>

<div

class

=

"large-10 small-10 columns"

>

<div

class

=

"row"

>

<span

class

=

"large-12"

>{{ ngModel.title.$text }}</span>

</div>

<div

class

=

"row"

>

<div

class

=

"small-1 columns"

></div>

<div

class

=

"small-2 columns push-8"

><a href=

"{{ ngModel.link[0].$text }}"

>Link</a></div>

</div>

</div>

</span>

</div>

注意我們在模版檔案裡用ngModel來指向之前的program資料,因為在建立自定義指令屬性時,我們做了設定。

現在,我們在主HTML檔案裡就不用再寫上面那麼多HTML,而隻要簡單地換上我們的自定義指令屬性npr-link:

1 2 3 4 5

<ul id=

"programs_list"

class=

""

>

<li ng-repeat=

"program in programs"

>

<span npr-link play=

'play(program)'

ng-model=

"program"

></span>

</li>

</ul>

在下一章中,我們會介紹Service的用法,以及如何在我們應用的多個controller之間通訊。

本系列的官方代碼庫可從GitHub上下載下傳:

https://github.com/auser/ng-newsletter-beginner-series.

要将這個代碼庫儲存到本地,請先確定安裝了git,clone此代碼庫,然後check out其中的part5分支。我們使用XHR擷取模版,是以你需要在本地伺服器上運作這一章的代碼。在part5分支裡我們提供了伺服器端代碼:

1 2

git clone https:

//github

.com

/auser/ng-newsletter-beginner-series

.git git checkout -b part5

.

/bin/server

.sh