首先在 src/fooPackage 下新增一個 class,名字叫做 MyViewModel,內容是:
package fooPackage;
import org.zkoss.bind.annotation.Init;
public class MyViewModel {
private String bindF;
private String loadF;
private String saveF;
public MyViewModel(){
System.out.println("constructor");
}
@Init public void init(){
System.out.println("init");
bindF = "bindV";
loadF = "loadV";
saveF = "saveV";
}
public String getBindF() {
System.out.println("getBindF");
return bindF;
}
public void setBindF(String value) {
System.out.print("setBindF : "+value);
this.bindF = value;
System.out.println(" after set : "+this.bindF);
}
public String getLoadF() {
System.out.println("getLoadF");
return loadF;
}
public void setLoadF(String value) {
System.out.print("setLoadF : "+value);
this.loadF = value;
System.out.println(" after set : "+this.loadF);
}
public String getSaveF() {
System.out.println("getSaveF");
return saveF;
}
public void setSaveF(String value) {
System.out.print("setSaveF : "+value);
this.saveF = value;
System.out.println(" after set : "+this.saveF);
}
}
雖然看起來毫無反應、就只是個平凡的 Java POJO,不過在 ZK MVVM 中,我們把這樣的 class 稱為 View Model。接下來當然是寫一個 index.zul,內容為:
<?page title="index.zul"?>
<window apply="org.zkoss.bind.BindComposer" viewModel="@id('vm') @init('fooPackage.MyViewModel')">
Bind Field : <textbox value="@bind(vm.bindF)" /> <separator />
Load Field : <textbox value="@load(vm.loadF)" /> <separator />
Save Field : <textbox value="@save(vm.bindF)" />
</window>
瀏覽 index.zul 時,我們可以從 server 的 console 輸出發現下面這幾行:
constructor
init
getBindF
getLoadF
畫面上則是在「Bind Field」與「Load Field」的欄位上分別出現「bindV」與「loadV」,而「Save Field」則仍然是空白,這代表什麼意思呢?
首先,ZK 在處理 index.zul 時,會依照 viewModel 的值,建立一個 MyViewModel 的 object,並給予變數名稱 vm,然後呼叫當中 annotation 為 @init 的 method(也就是 init())。接著處理到 @bind(vm.bindF) 與 @load(vm.loadF) 時會呼叫 MyViewModel 的 getBindF() 與 getLoadF() 取得對應的值來顯示。那麼,@bind 與 @load 又有什麼區別呢?
現在回到瀏覽器上,將「bindV」後頭輸入「keyin」,當游標離開 textbox 時(觸發 onBlur),我們發現 server 的 console 輸出:
setBindF : bindVKeyin after set : bindVKeyin
getBindF
同樣對「loadF」輸入「Keyin」則完全沒反應,而對「saveF」輸入則出現:
setSaveF : Keyin after set : Keyin
你可能已經有些頭緒了,@bind、@load、@save 分別定義了 ZUL(View)與 ViewModel 之間資料傳遞的關係:
* @load 僅能讓 ZUL 從 ViewModel 取資料
* @save 則是僅能讓 ZUL 將資料傳回至 ViewModel
* @bind 則兩者均可。
那麼,如果想要在 ZUL 中更新 @load 的值,該怎麼作呢?答案是 @NotifyChange 這個 annotation。讓我們將 MyViewModel 的 field loadF 還有 setLoadF() 拿掉,然後把 getLoadF() 改成:
public String getLoadF() {
System.out.println("getLoadF");
//reverse saveF
StringBuffer result = new StringBuffer();
for(int i=saveF.length()-1; i>=0; i--){
result.append(saveF.charAt(i));
}
return result.toString();
}
最後在 setSaveF() 前面加上 @NotifyChange("loadF"),重新瀏覽 index.zul,我們會發現「Load Field」的值一開始是「Vevas」(將 saveV 倒過來),如果你在「Save Field」當中輸入「123」,則「Load Field」會變成「321」 (即使你很愛 ABBA 或是拿破崙,也不要輸入「ABBA」或是「able was i ere i saw elba」... 拜託 Orz) 。這是因為 setSaveF() 加上 @NotifyChange("loadF") 之後,只要呼叫到 setSaveF()(在這裡是「Save Field」的 textbox 觸發 onBlur),就會在執行完畢之後,重新觸發一次 @load(vm.loadF),也就會呼叫 getLoadF() 會回傳新的「saveF」內容值的反字串。
@NotifyChange 後頭的參數還有幾種變形:@NotifyChange({"bindF", "loadF"}),當你一次想更新兩個以上的 field,就用這招。如果所有的 field 都想更新,又不想一個一個 keyin 進去怎麼辦?別害怕,有 @NotifyChange("*") 可以滿足你,不過在目前這個 case 中,跟 @NotifyChange({“bindF”, “loadF”}) 的效果是一樣的。至於還有一個 @NotifyChange(".") 就留給你當回家作業了......
No comments:
Post a Comment