歡迎轉載(註明出處)或直接轉貼網址也ok,但是請不要直接把內容摳走貼在別的地方~

這篇實在寫太久了.....學得十分緩慢,雜事實在太多....

但有學到總是好的,所以照慣例記錄一下學習心得

 

以下開始前言 : 

ng-repeat 這個 directive 前幾篇有偷用過很多次了~

這篇要來學習的是 ng-repeat 的一般與進階用法,還有使用時的注意事項

然後這篇決定將程式碼上色一下,不然有點難閱讀....= =

然後下面的程式碼都可以切換到 result 頁簽看執行結果

 

一樣有目錄 

1. ng-repeat 使用方式

2. DOM的產生,與 ng-repeat-start、ng-repeat-end 的使用 

3. 使用ng-repeat 會遇到的問題,與 track by (追蹤)

4. 使用 orderBy 對 ng-repeat 進行排序

5. 使用 limitTo 限制 ng-repeat 的筆數

6. 使用 filter 過濾器來過濾來源資料

 

ng-repeat 使用方式

官網文件在這邊 : 

https://docs.angularjs.org/api/ng/directive/ngRepeat

ng-repeat 主要功能就是將樣板中的內容依照來源的資料,重複產生DOM元素

ˊ而資料的來源分成兩種,陣列或物件 

1. 陣列

HTML

<body ng-app="">
<div ng-repeat="x in [1,2,3]">
{{ x }}
</div>
</body>

執行後就會出現 : 

1
2
3

或是像下方這樣陣列在 controller 裡面

 

2. 物件

因為物件會有 key 跟 value 值,所以是用以下的方法抓取資料

HTML : 

<div ng-app="myapp" ng-controller="mycontroller as ctrl">
<div ng-repeat="(key,value) in ctrl.school">
姓名:{{ key }} <br />
年齡:{{ value.age }} <br />
體重:{{ value.weight }} <br /><br />
</div>
</div>

JS : 

angular.module("myapp",[]).controller("mycontroller",[function() {
var self = this;
self.school = {
"john": {
"age": 18,
"weight": 50
},
"jack": {
"age": 19,
"weight": 53
},
"mary": {
"age": 20,
"weight": 43
}
}
}]);

結果就會像下面這樣 : 

ng-repeat 也有提供幾個特殊的變數,如下面官網的資料

VariableTypeDetails
$index number iterator offset of the repeated element (0..length-1)
$first boolean true if the repeated element is first in the iterator.
$middle boolean true if the repeated element is between the first and last in the iterator.
$last boolean true if the repeated element is last in the iterator.
$even boolean true if the iterator position $index is even (otherwise false).
$odd boolean true if the iterator position $index is odd (otherwise false).

底下就來 try try 看 

1. $index

$index 就是目前索引跑到了第幾個項目,索引是從0開始,直接看範例最清楚了

用剛剛的範例再加上 {{ $index + 1 }} 來跑出序號

2. $first 

$first 指的是迴圈中的第一列,如果是第一列就是 true 

所以可以用這個 $first 來判斷是不是第一筆資料,下面例子順便用上了上一篇的 ng-class

3. $middle  

$middle 指的是迴圈中間的內容,所謂中間的內容就是排除 $first 第一筆,與排除 $last 最後一筆

範例一樣透過 ng-class 來表示囉

如果剛好是迴圈中間的資料 $middle 就會是 true,然後就會套用 middle 這個 class

4. $last

$last 指的就是所有資料的最後一列 

5. $even

$even  指的是偶數列,但要注意索引是從 0 開始喔

6. $odd

$odd 指的是奇數列

 

DOM的產生,與 ng-repeat-start、ng-repeat-end 的使用

接下來要來看一下 ng-repeat 實際是怎麼產生 DOM 的,如以下的範例

<body ng-app="">
<div ng-repeat="x in [1,2,3]">
{{ x }}
</div>
</body>

執行後變這樣,再檢視原始檔可以發現,ng-repeat 是重複指示詞所在的那個 html 標籤本身

除了重複產生 DOM,連原本的 ng-repeat 屬性都會一併複製,還順便多了 class="ng-binding ng-scope" 

Clipboard01   

其實我剛開始使用的時候一度以為他是重複跑指示詞所在html標籤內的子元素

可能 .net 寫習慣了.....net 的 repeat 是跑樣板內的內容,就像下面這樣

<asp:Repeater ID="repeater" runat="server">

<ItemTemplate>

<div><%#Eval("DisplayName") %></div> // 人家會重複的只有這行

</ItemTemplate>

</asp:Repeater>

既然搞清楚 ng-repeat 是重複所在的標籤本身 + 其內部的子元素,現在又衍伸一個另外的問題

因為有時候要重複的區塊可能是多個,而且不包含在 ng-repeat 所在的標籤容器內

例如以下的例子 : 

<div ng-app="myapp" ng-controller="mycontroller as ctrl">
<table>
<tr ng-repeat="(key,value) in ctrl.school">
<td>姓名:</td><td>{{ key }}</td>
</tr>
<tr><td>年齡:</td><td>{{ value.age }}</td></tr>
</table>
</div>

執行後就會變這樣

================

姓名: bill
姓名: jack
姓名: john
姓名: mary
年齡:  
 

 

但其實我是想要這樣

================

姓名: bill
年齡: 22
姓名: jack
年齡: 19
姓名: john
年齡: 18
姓名: mary
年齡: 20

 

但是因為只能重複一個 tr 本身跟他的子元素, 所以要做到想要的結果有點困難

所以 ng-repeat 有提供了另一種方法來解決這個問題,就是使用 ng-repeat-startng-repeat-end

ng-repeat-start 開始到 ng-repeat-end 結束的範圍內都會被複製,並且都可以取得 repeat 的來源物件

所以上面的範例改成這樣就解決了

<div ng-app="myapp" ng-controller="mycontroller as ctrl">
<table>
<tr ng-repeat-start="(key,value) in ctrl.school">
<td>姓名:</td><td>{{ key }}</td>
</tr>
<tr ng-repeat-end><td>年齡:</td><td>{{ value.age }}</td></tr>
</table>
</div>

完整範例如下

 

使用ng-repeat 會遇到的問題,與 track by (追蹤)

上面有提到,ng-repeat 會反覆瀏覽物件或陣列中的每個值,用來產生新的 DOM 元素

但是為了效能最佳化會根據物件的 hash (AngularJS 所決定的) 來判斷是否要使用原有DOM元素或是重新產生 DOM 元素

上面這段話可能有點難懂

但其實你用來跑 ng-repeat 的來源"物件"都會被加上一個 $$hashKey 的值,用來跟產生的 DOM 作對應

有了$$hashKey 跟DOM 對應,AngularJS 才會知道異動的是那些資料,然後來決定是否要重新產生 DOM元素

看一下以下的例子 : 

在按下"刪除最後一筆"的時候雖然被刪除了,但其他 $$hasKey 的值都沒改變

按下重新取得後,因為已經是不同執行個體了,所以AngularJS 就判斷需要重新產生

然後物件會有 $$hashKey,但陣列預設是用陣列的內容來當 key 值的喔 (陣列也沒法附加延伸屬性)

所以如果你的陣列中有內容重複的話,如下有兩個 1 重複了

<div ng-app="">
<div ng-repeat="n in [1,2,3,1,5]">
{{ n }}
</div>
</div>

就會發生如下的錯誤訊息 : 

angularjs_ng-repeat_error     

因為對 ng-repeat 來說,能夠辨別的索引鍵重複了

針對這種狀況 AngularJS 提供了 track by 來設定 ng-repeat 的鍵值 (追蹤ID)

下面的 code 就設定了上面提到的 $index 變數來當 track id

<div ng-app="">
<div ng-repeat="n in [1,2,3,1,5] track by $index">
{{ n }}
</div>
</div>

 結果就會正確的如下所顯示了

1
2
3
1
5

如果你的物件來源是資料庫,也可以指定成資料表中的索引值,ex : track by obj.id 來確保不會重複

除了以上方法也可以使用 track by $id( $index)$id() 則是會將傳入的物件回傳出一個對應的雜湊資料

使用 track by 除了解決造成錯誤訊息的問題,也可以減少頻繁的重複更新DOM元素,進而提高效能

可以參考一下高手們寫的這幾篇 : 

NG筆記22-避免ngRepeat重新產生DOM元素

AngularJs ng-repeat 必須注意的性能問題

或是參考以下自己仿照測試的範例

加上 track by 之後就可以讓DOM重複被利用了

 

使用 orderBy 對 ng-repeat 進行排序

ng-repeat 可以加上 orderBy 來幫來源資料進行排序,如以下的範例 :

<div ng-app="myapp" ng-controller="mycontroller as ctrl">
<!--  由小到大排序 -->
<div ng-repeat="n in ctrl.note | orderBy:'id'" ng-bind="n.id"></div>
<!--  由大到小排序,要排序的欄位加上負號 -->
<div ng-repeat="n in ctrl.note | orderBy:'-id'" ng-bind="n.id"></div>
<!--  由小到大排序,後面加上 boolean -->
<div ng-repeat="n in ctrl.note | orderBy:'id':false" ng-bind="n.id"></div>
<!--  由大到小排序,後面加上 boolean -->
<div ng-repeat="n in ctrl.note | orderBy:'id':true" ng-bind="n.id"></div>
</div>

結果就會如下 : (自己切換到 result 頁簽看結果)

 

使用 limitTo 限制 ng-repeat 的筆數

ng-repeat 可以加上 limitTo 來限制資料的筆數,像下面的範例 : 

<div ng-app="myapp" ng-controller="mycontroller as ctrl">
<div ng-repeat="n in ctrl.note | orderBy:'id' | limitTo:3" ng-bind="n.id"></div>
<br /><br />
<div ng-repeat="n in ctrl.note | orderBy:'-id' | limitTo:-2" ng-bind="n.id"></div>
</div>

加上負號就是從後面開始取資料

使用 filter 過濾器來過濾來源資料

ng-repeat 也可以使用 filter 來過濾資料

例如以下這樣 : 

其他更詳細的 filter 用法之後再學邊寫囉~~~

 

AngularJS 學習筆記系列 : (依本人的上進心持續增加)

AngularJS 初學者筆記與教學 (一) - 使用方式、Expression、Controller、Module

AngularJS 初學者筆記與教學 (二) 內建的簡單指令之1 ng-click、ng-show、ng-hide

AngularJS 初學者筆記與教學 (三) - AngularJS 的載入流程與 ng-cloak 運作原理 

AngularJS 初學者筆記與教學 (四) - 如何動態改變 CSS 樣式,使用 ng-class、ng-style

AngularJS 初學者筆記與教學 (五) - 繼續深入學習常用的 ng-repeat

AngularJS 初學者筆記與教學 (六) - 單元測試

AngularJS 初學者筆記與教學 (新手入門課程 - 課後心得篇)

AngularJS 初學者筆記與教學 (七) - FormController 的表單驗證與資料檢查

 

創作者介紹
創作者 小雕 的頭像
小雕

小雕雕的家

小雕 發表在 痞客邦 留言(1) 人氣()


留言列表 (1)

發表留言
  • Aaron
  • 感謝分享! 受益良多 : )
  • 不客氣 ^^ 我也還在學習中,彼此加油~

    小雕 於 2015/06/12 14:48 回覆