ModalWindow内のページとデータをやり取りするときの落とし穴



(Wicket 6.4.0にて確認した現象)

# ModalWindow表示前後、呼び出し元Pageクラスのインスタンスは異なる


ModalWindowが表示されている間、呼び出し側のPageクラスは直列化され、ModalWindowが閉じられるときに復元されるようです。

つまり、ModaoLWindow表示前後で呼び出し側Pageクラスの __  インスタンスは異なっています  __  。

これを意識せずにデータをやり取りするプログラムを書いてしまうとはまります。

# 検証コード

ModalWindowでリンクを押した時刻を呼び出し元に表示するプログラムで検証します。

## NGコード

まずは呼び出し側のページのコード。

```ParentPage.java
public class ParentPage extends WebPage {

    private Date  now = Calendar.getInstance().getTime();
    private Label nowLabel;

    public ParentPage() {
        // 時刻を表示するラベル
        this.nowLabel = new Label("now", new AbstractReadOnlyModel<String>() {
            @Override
            public String getObject() {
                return DateFormat.getDateTimeInstance().format(now);
            }
        });
        this.nowLabel.setOutputMarkupId(true);
        this.add(this.nowLabel);

        // ModalWindow
        final ModalWindow modalWindow = new ModalWindow("modalWindow");
        modalWindow.setPageCreator(new PageCreator() {
            @Override
            public Page createPage() {
                final ChildPage.Callback callback = new ChildPage.Callback() {
                    @Override
                    public void callback(AjaxRequestTarget pTarget, Date pNow) {
                        // ModalWindowでリンクが押された時刻を記録してダイアログを閉じる
                        ParentPage.this.now = pNow;
                        modalWindow.close(pTarget);
                    }
                };
                return new ChildPage(callback);
            }
        });
        modalWindow.setWindowClosedCallback(new WindowClosedCallback() {
            @Override
            public void onClose(AjaxRequestTarget pTarget) {
                // ModalWindowが閉じられたタイミングで時刻ラベルを再描画
                // (ブラウザ上の表記が更新されることを期待)
                pTarget.add(nowLabel);
            }
        });
        this.add(modalWindow);

        // ModalWindowを表示するためのリンク
        AjaxLink<?> shower = new AjaxLink<Object>("shower") {
            @Override
            public void onClick(AjaxRequestTarget pTarget) {
                modalWindow.show(pTarget);
            }
        };
        this.add(shower);
    }
}

```

内部クラスが多く見づらいかもしれませんが、ポイントは以下の部分です。

```java
public void callback(AjaxRequestTarget pTarget, Date pNow) {
    // ModalWindowでリンクが押された時刻を記録してダイアログを閉じる
    ParentPage.this.now = pNow;
    modalWindow.close(pTarget);
}
```

特に違和感のないコードだと思います。

呼び出される側のページクラスは以下の通りです。

```ChildPage.java
public class ChildPage extends WebPage {

    public ChildPage(final Callback pCallback) {
        AjaxLink<?> link = new AjaxLink<Object>("link") {
            @Override
            public void onClick(AjaxRequestTarget pTarget) {
                // リンクが押されたことを現在時刻と共に呼び出し元に通知
                pCallback.callback(pTarget, Calendar.getInstance().getTime());
            }
        };

        this.add(link);
    }

    /**
     * リンクが押されたことを通知するコールバック.
     */
    public interface Callback extends Serializable {
        void callback(AjaxRequestTarget pTarget, Date pNow);
    }
}
```	

しかし、このコードは期待どおりに動作しません。

時刻ラベルが更新されないのです。


## 解決版コード：```PageReference```を使う

この問題を解決するには、```PageReference```を使います。

先ほどのポイント部分のコードを以下のように書き換ると期待どおり動作するようになります。

```java
public void callback(AjaxRequestTarget pTarget, Date pNow) {
    // ModalWindowでリンクが押された時刻を記録してダイアログを閉じる
    ParentPage p = (ParentPage) ParentPage.this.getPageReference().getPage();
    p.now = pNow;
    modalWindow.close(pTarget);
}
```

（ただし、ModalWindow表示中に呼び出し元ページを再描画しようとするとエラーになります。）


# ```PageReference```ってなにもの？

```PageReference``` クラスは、このように、かなり重要なクラスのように思えるのですが、APIリファレンスは非常にあっさりしています。

http://ci.apache.org/projects/wicket/apidocs/6.x/org/apache/wicket/PageReference.html

もう少し親切だったらいいのになぁ。