來寫一篇 ZK-1310 的檢討報告 (誰叫我亂發 issue) 。
起因是這一個 forum thread。
在這裡先 murmur 一下,ZK 截至目前為止是沒有 用 MVVM 寫一個 Tree 的官方範例;
MVC 版是有,可是... 只能說那個建立 TreeModel
的 code 實在是太銷魂了,
而且在我修改之前的程式碼根本不能跑。
另外也很好奇,自己從 MVC 版改裝成 MVVM 有那麼難嗎?
為什麼一堆歪果人在 forum 上頭掱呢?果然付錢用 ZK 跟拿錢用 ZK 差很多 [淚目]
啊啊... 是檢討報告、不是 complain 大會... [逃]
故事到底是怎麼發生的呢?當你用 MVVM 操作一個 Tree
:
<div apply="org.zkoss.bind.BindComposer"
viewModel="@id('vm') @init('org.zkoss.rtmf.vm.TreeVM')">
<tree checkmark="true" model="@load(vm.treeModel)">
<template name="model" var="foo"><treeitem>
<treerow>
<treecell label="@load(foo.data.id)"></treecell>
</treerow>
</treeitem></template>
</tree>
</div>
而 TreeVM
長這樣:
public class TreeVM{
private DefaultTreeNode<Product> selectedProduct;
public DefaultTreeNode<Product> getSelectedProduct() {
return selectedProduct;
}
@NotifyChange("selectedProduct")
public void setSelectedProduct(DefaultTreeNode<Product> selectedProduct) {
this.selectedProduct = selectedProduct;
}
public DefaultTreeModel<Product> getTreeModel(){
DefaultTreeModel<Product> result = MockData.genTreeModel();
int[] path = {0};
result.addSelectionPath(path);
return result;
}
}
在 getTreeModel()
另外用 addSelectionPath()
設定哪一個 item 是 selected 的。
實際運作起來完全正常、第一個 item 會變成 selectedItem。
好的,如果打算讓 VM 去控制 selectedItem,
那麼很直覺地就掛上 selectedItem
這個 attribute,像這樣:
<tree checkmark="true" model="@load(vm.treeModel)" selectedItem="@bind(vm.selectedProduct)">
然後小美家就爆炸了第一個 item 不會變成 selectedItem 了。
為甚麼? <囧>
原因說起來很簡單也很複雜,為了賺不知道在哪裡的稿費,我用複雜一點的方式講 XD。
再更細節我也不太清楚,總之可以確定的是 model
跟其他 attribute 不太一樣,
它會像有特權一樣、無視於 attribute 的順序、甚至應該是脫離傳統程序來處理。
所以,算是順帶一提,如果像下面這個沒有用 model 決定內容的 <listbox>
,掛上 selectedIndex
:
<listbox id="lbx">
<listitem><listcell label="1" /></listitem>
<listitem><listcell label="2" /></listitem>
<listitem><listcell label="3" /></listitem>
</listbox>
<button onClick='lbx.setSelectedIndex(2)' label="selectedIndex=2"/>
就會炸 error:
Out of bound: 2 while size=0
這是因為在 ZK 處理到 <listbox>
的其他 attribute 時還不知道有幾個 children(<listitem>
);
如果拿掉 selectedIndex="1"
,然後選完 item 之後按 show selectedIndex
這個 button,卻沒有問題,
因為這個時候 <listbox>
已經知道。而如果是透過 model
來設定就沒有上述問題,因為它耍特權...... Orz。
交待完黑頁,現在來回歸劇情主軸。所以那個好像出問題的程式,其實運作的順序大概是這樣的:
- ZK parse 到
<tree>
- 發現有 model,優先處理
- 從
TreeVM.getTreeModel()
取得資料 - 根據資料設定
<tree>
的內容
- 從
- 處理
checkmark
這個 attribute - 處理
selectedItem
這個 attribute- 從
TreeVM.getSelectedProduct()
取得資料(是 null) - 對
<tree>
設定setSelectedItem(null)
- 從
於是在 TreeVM.getTreeMode()
當中千辛萬苦設定的 addSelectionPath()
就被蓋掉了 [淚目]。
這樣看起來完全合情合理,壓根不是什麼 bug,
因為只要 TreeVM
在第一次呼叫到 getSelectedProduct()
之前有把 selectedProduct
設定正確就好。
在這個 case 當中,最方便的作法當然就是 getTreeModel()
在 result.addSelectionPath(path);
後頭加上
selectedProduct = result.getChild(path);
就天下太平了。太平歸太平,但是上面這一串,說實在還是有點簡略,因為省略過 view、model、view model 之間互相影響的細節。 單就 ZK-1310 來說,應該已經足夠了。
所謂「江湖一點訣,說出不值錢」,如果當初文件上有記上這一筆, 也就不至於讓一個 developer 去發 forum、讓一個笨蛋 ZK Engineer 跑去發 issue、 最後讓另一個 ZK Engineer 花上半個多小時去思考這個問題。
阿們...... [合掌]
No comments:
Post a Comment