AngularJS 筆記: ngRepeat


現在JSFiddle可以嵌入Blogger內,所以從其他筆記軟體跳過來使用Blogger紀錄一些程式相關的筆記,真的方便許多。




這筆記所使用的AngularJS版本為 1.4.8



AngularJS中 ng-repeat是很常用到的directive,主要用來顯示重複的內容,像是產生table的row,原本覺得滿容易上手的,不過網路上查過後發現有許多我完全不知道的部分,只能靠筆記來幫助自己記憶。

  • 基本使用方法


ng-repeat=" (key, value) in dataset"

ng-repeat=" obj in dataset"


透過第一個方法,AngularJS會逐一取出dataset中的key跟value供使用
第二個方法則是直接取出value,兩者分別可以用在不同的情況。

用在陣列上
使用第一個方法,取出的key為陣列的index,value則是dataset[index]
使用第二個方法取出的obj 即為 dataset[index]


用在物件上,則可以取出物件內的屬性與對應的值
第一種方法的key可以直接取出物件內的attribute name,value則是對應的值
第二種方法則是直接將value取出來


如果是物件陣列,則可以逐一取出物件,再透過取出的物件存取內容
第一種方法拿到的key 即為index,value可以取得 dataset[ index]的物件
第二種方法則是直接取得dataset[ index]的物件
要取得物件內所有屬性內容,則使用前一個方法,或是直接使用 value.attributeName即可


到上面其實已經可以解決許多需求,不過網路上查了之後才知道還有許多有用的部分

  • 特殊變數
ng-repeat每一次的iteration都會建立一個template,每個template內都有獨立的scope,而這些獨立的scope都會帶有幾個local variable,用來儲存當時iteration的資訊。

  1. $index: 即當下iteration的index,內容從0到dataset.length-1
  2. $firsttrue:目前位於dataset第一筆資料;false:目前不是第一個
  3. $lasttrue:目前位於dataset最後一筆資料;false:目前不是最後一個
  4. $middle:$first與$last以外iteration都是true
  5. $oddtrue:$index為奇數;false:$index為偶數
  6. $eventrue:$index為偶數;false:$index為奇數

直接從程式來看會比較清楚

可以透過這些變數的幫助,更靈活的使用ng-repeat。

  • ng-repeat-start與ng-repeat-end
上面所使用的ng-repeater都是針對element自己與children做repeat的動作,像是table中的tr與td,
我們將ng-repeat設在tr上,讓tr與td的內容根據dataset的內容重複顯示

 但如果我們希望可以透過 table的方式顯示像下面的內容
id: 0

name: John

id: 1

name: Tom

將會發現光用ng-repeat似乎無法達到效果,
之前所使用的都是在一個重複在一個element與children上
如果希望可以repeat 好幾個element,則是要透過ng-repeat-start與ng-repeat-end兩個directive

 使用方法
element1 ng-repeat-start="data in dataSet"
child1
child2
element2
child1
child2
element3 ng-repeat-end
child3

值得關注的是,透過ng-repeat-start與ng-repeat-end,
上面所見到的所有element在每個iteration都會repeat,包含element1與其children、element2與其children與element3與其children

從ng-repeat-start開始,到ng-repeat-end(包含加上end directive的element)結束,被這兩個directive包起來的部分都會是repeat的內容。

從範例來看,Job的row 是ng-repeat-end所放置的位置,其也會列入repeat的項目內。


  • track by
在ng-repeat中,每一個iteration都會產生一個DOM每一個DOM都有一個唯一的key,ng-repeat透過這唯一的key來對應到各個DOM。

以陣列來看,如果是單純的陣列(陣列裡面不是放物件),其產生出來的DOM對應的key為陣列的值
var array1=[1, 2, 3, 4, 5];  //以陣列內容當作DOM的key
var array2=['A','B', 'C', 'D'];  //以陣列內容當作DOM的key

像是上列類型的陣列,array1[0]產生的DOM,他的key就是1, array[1]產生的DOM,key則為2
如果是array2[0]產生的DOM則為A,array2[1]的DOM為B,以此類推。

但是剛剛有說到DOM對應到的key必須是唯一的,但是陣列中的內容有可能會重複,
如果陣列內容有重複會發生,就會發生錯誤,打開colsole line可以看見AngularJS報的錯誤訊息:

Error: [ngRepeat:dupes] Duplicates in a repeater are not allowed. Use 'track by' expression to specify unique keys. 


上面出現的錯誤訊息有重要的關鍵字,Use 'track by' expression to specify unique keys. 剛剛提到上列的陣列是以陣列的內容當做DOM的key,所以可能會有重複的狀況,所以AngularJS提供了一個track by的方式,讓使用者可以自己指定DOM的key,像是上列的陣列,其實可以以透過先前提到的變數$index作為key。

track by的用法很簡單,搭配ng-repeat使用,要注意的是,track by一定要放在整個expression的最後面。

ng-repeat =" data in dataSet track by uniqueKey"
如此就可以將剛剛的程式改過,並正常運行。
 
上面提到的都是單純的陣列,如果是物件陣列,像是
var objArray=[{id: 1, name: 'John'},
                      {id: 2, name: 'Tom'},
                      {id: 3, name: 'Steve'} ]; 
類型的陣列,其DOM的key與前面的陣列又有不同,AngularJS會將陣列中的物件,丟進一個hash function中,產生的結果就會放在$$hashKey的變數中,$$hashKey就是各DOM的key,我們可以透過觀察$$hashKey來得知每個DOM對應得的key是什麼。
雖然AngularJS有這樣的機制來產生DOM的key,但還是建議使用track by 加上物件屬性的id或是key會比較妥當,會建議這麼做主要是跟ng-repeat的效能有關係,透過指定唯一的key來避免讓AngularJS重複產生新的Dom,並且善用已經有的DOM。詳細可以參考避免ngRepeat重新產生DOM元素

上面的程式碼可以改成track by data.id,執行後會發現$$hashKey沒有值,因為只要track by指定了key的來源,AngularJS就不會再將陣列中的物件丟進hash中產生$$hashKey
 
ng-repeat的筆記大約到此,日後如果還有學到其他的相關內容會在同一篇繼續往下再補充。



學習過程參考文章
AngularJS官網
初學者筆記與教學-(五)---繼續深入學
Day7- 入門AngularJS筆記-AngularJS指令(6): ng-repeat


參考書
AngularJS建置與執行

沒有留言:

張貼留言

Java Spring Framework 筆記 - Autowiring (2)

這篇記錄透過 Annotation來做到 Spring的 autowiring。