寫 web application,常常會遇到 Form 裡面需要放多個 submit button。在 Wicket 裏,我們可以使用 Button component:
Form myForm = new Form("myForm") ;
myForm.add(new Button("ok") {
@Override
protected void onSubmit() {
logger.info("ok pressed");
}
});
myForm.add(new Button("cancel") {
@Override
protected void onSubmit() {
logger.info("cancel pressed");
}
});
每個 button 裡的 onSubmit() 就是按下後要做的事,很直覺簡單吧?通常使用多個 button 時,多半只有一個 button 是真的進行 submit,其他的 button 可能是做別的操作:例如 cancel、多個欄位、或是去資料庫撈資料... 等等。這樣的 button 通常在 submit 之後 -- (1) 不做 validation (2) 不更新背後的 domain 物件 (3) 但要保留使用者的輸入值 -- 算是有點麻煩了,花了一個晚上終於查到了寫法:
public class KeepFormStateButton extends Button {
public KeepFormStateButton(String id) {
super(id);
//設定為 false 後,便不做 validation 和 更新背後的 domain 物件
setDefaultFormProcessing(false);
}
public KeepFormStateButton(String id, IModel model) {
super(id, model);
setDefaultFormProcessing(false);
}
@Override
protected final void onSubmit() {
keepFormState();
onSkipProcessSubmit();
}
private void keepFormState() {
// recursive 的找出所有的 FormComponent,並全部設為 invalid。
// FormComponent 設為 invalid,它便會保留使用者原本輸入的值。
getForm().visitFormComponents(new FormComponent.IVisitor() {
public void formComponent(final FormComponent formComponent) {
if (formComponent.isVisibleInHierarchy()) {
formComponent.invalid();
}
}
});
}
//subclass need override this to provide custom action
protected abstract void onSkipProcessSubmit();
}
這個 KeepFormStateButton 是個 reusable 的 component。Subclass 之後,只要覆寫 onSkipProcessSubmit() 即可,其他的動作都已經寫好了:它會跳過 validation、update data、並且利用 invalid() 來保留原輸入值。用 invalid() 其實算是走後門... 我們假裝使用者輸入的所有的值都是錯誤的,當 wicket 內部運作遇到狀態為 invalid 時,自然會替 form 保留原值了。這並不是正統解法啦... 目前 Wicket Mailing List 也在討論如何正確的保留原值...
Anyway,看 open source 的 code 其實蠻好玩的,可以學到不少技巧,像是 Wicket 的開發者就很喜歡用 inner class。上面的 getForm().visitFormComponents(new FormComponent.IVisitor() .... 就是用來 recursive 地拜訪所有的子 component (呈階層樹狀的結構)。用 anonymous class + call back 這招還真是絕啊!一般常看到多半寫個 recursive 的 method 來達到的說... 嗯嗯,給他偷學起來 :-)
(ps. 這種寫法在其他語言裡 (ruby...) 應該是 closure 吧?沒啥接觸...)