13 October 2005

寫 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 吧?沒啥接觸...)


回響

可以用 Tag <I>、<B>,程式碼請用 <PRE>