利用avalon 實現一個簡單的成績單
本文的靈感是來自Halower的這篇博文,他是使用knockout與jQuery實現的。不過我覺得MVVM本來就強大的事件綁定功能,因此用jQuery 是多此一舉。另,他也用了一些面向對象的寫法。我個人認爲,純數據就該好好當純數據,作爲數據模型(M)而存在,想操作數據,則交由視圖模型(VM)。在angularjs流行的一些成規,都是要求大家不要自己操作DOM,DOM是框架自行幫你偷偷搞定。這也與avalon一直提倡的“操作數據即操作DOM”的理念相符。由於avalon巧妙地利用了Object.defineProperty, __defineSetter__, __defineGetter__, VBScript等方法把等於號(=)重載了,因此與視圖的同步就變得比其他MVVM更隱祕神奇。那麼接着下來,讓我們看看avalon是如何實現這個功能吧。
首先是視圖層,裏面的綁定屬性其實可以在VM中的屬性定了下來再添加。這也涉及MVVM另一個優勢,分離關注點,因此切圖與JS編程可以同時進行。由於JS代碼不進行DOM操作,頁面長得怎麼樣也無所謂,我又不需要選擇器引擎!
< div ms-controller = "grid" class = "grid" > |
< div > |
< p >
< input ms-duplex = "id" > |
< input ms-duplex = "name" > |
< input ms-duplex = "score" data-duplex-event = "change" ></ p > |
< p >< button ms-click = "add" >
add</ button ></ p > |
</ div > |
< p >共{{array.size()}}條------------------合計{{total}}分</ p > |
< table > |
< thead > |
< tr > |
< th >ID</ th >
< th >姓名</ th >
< th >分數</ th >
< th >操作</ th > |
</ tr > |
</ thead > |
|
< tbody ms-each-el = "array" > |
< tr > |
< td >{{el.id}}</ td > |
< td >{{el.name}}</ td > |
< td >{{el.score}}</ td > |
< td align = "center" >< a ms-click = "$remove" href = "javascript:void(0)" >移除</ a ></ td > |
</ tr > |
</ tbody > |
</ table > |
< textarea ms-value = "JSON.stringify(array.$model)" style = "width:90%;height:220px;" ></ textarea > |
</ div > |
樣式隨便弄弄:
.grid
table{ |
border : 1px solid #000 ; |
width : 500px ; |
border-collapse :
collapse ; |
} |
.grid
button{ |
width : 400px ; |
background :
orange; |
} |
.grid
table th, .grid table td{ |
border : 1px solid #000 ; |
padding :
2px 5px ; |
} |
從HTML結構來看,分爲兩部分,一個是用於輸入數據,另一個是呈現所有數據,數據上方還有個小統計。輸入數據部分有三個輸入項,我們對分數中進行校驗,只保證其是數字就行了,目的是爲了相加,因爲input的value屬性總爲一個字符串類型。下方有個按鈕,用於提交。呈現區爲一個table,所有MVVM框架都支持數組循環輸出,我的與angular走得很近。在循環區域,裏面還內置一個$remove方法,用於刪除監控數組中的某一個元素,這個比knockout人性化多了。下面是JS部分,你是看不到一句操作DOM的代碼。
//如果大家對avalon不熟悉,可以參看這篇入門教程
http://www.cnblogs.com/rubylouvre/p/3181291.html |
avalon.ready( function ()
{ |
var model
= avalon.define( 'grid' ,
function (vm)
{ |
vm.id
= "" |
vm.name
= "" |
vm.score
= 0 |
vm.total
= 0 |
vm.add
= function ()
{ |
if (vm.id
&& vm.name ){ |
vm.array.push({ |
id:
vm.id, |
name:
vm.name, |
score:
vm.score |
}) |
} |
} |
vm.array
= [] |
}); |
model.$watch( "score" ,
function (a)
{ |
var a
= Number(a) || 0 |
a
= a > 100 ? 100 : a < 0 ? 0 : a //強制轉換爲0~100間 |
model.score
= a |
}) |
model.array.$watch( "length" ,
function ()
{ |
var a
= 0 |
model.array.forEach( function (el)
{ |
a
+= el.score //求得總數 |
}) |
model.total
= a; |
model.id
= "" |
model.name
= "" |
model.score
= 0 |
}) |
model.array
= [ |
{id:
"d1" ,
name: "李世民" ,
score: 67}, |
{id:
"d2" ,
name: "贏政" ,
score: 90} |
] |
avalon.scan(); |
}); |
我們在define方法中定義了VM所有用到的數據,什麼id, name, score, array, 還有需要綁到視圖中的add方法。數據校驗或數據變動時需要做的操作,我們是用$watch實現,它們被安排到define方法外,這是一個好主意。然後,就沒有然後了!這就是MVVM的神奇之處,因爲我們在視圖中使用了{{}}, ms-click已經指明瞭它們的行爲。因此當數據變動時,框架自然明白自己該什麼做。
目前我與5羣的一些人已經將avalon應用於公司的生產環境,反應還是不錯。雖然目前還不時冒出一些怪異的BUG,但難度不至於影響我們的進度,基本上半天就能修,比如說IE9-10的option標籤的value問題,firefox 全系列下,未插入DOM樹的元素的display樣式取值問題,這是jQuery也沒報到的新東西。有的話,我在做mass Framework時已經遇到過了。MVVM是一個全新的領域,要求對用戶全面隱藏DOM操作,並將整個DOM樹作爲一個動態模板或數個複用的子模板,另外,DOM樹其實也被我框架看作爲一個Ioc容器的配置文件。如此種種,遭遇新的問題在所難免,但只要方向是對的,這就是康莊大道。雖然它與jQuery走的是一條截然不同的路,但明顯優於jQuery。jQuery與DOM存在強依賴,導致維護成本奇高。這正是西方繼jQuery後,又孜孜不倦發明backbone, canjs, knockout, emberjs, angular的原因。現在國內的技術步伐普通比外國慢兩三年,現在前端MVVM已經在外國非常盛行,希望國人不要再落後太多了。