Click入門記 その4 〜 Pageから他のPage/URIへforward

ActionLinkなどコントロールのコールバック処理の結果、他の画面/機能に遷移したいことがあるでしょう。

機能を呼び出すのであれば Service的なインタフェースを呼びだせばいいだけの話ですが、画面遷移を伴う場合にはForward/Redirectが必要です。

Clickでは2つのforwardスタイル(フレームワーク支援), 1つのredirectスタイルを実装しています。加えて、ビューのhtmlファイルだけを差し替えるsetPath()というものがあります。

以下、一般的な事柄ですが押さえておきましょう。

  • forwardの場合はリクエスURIが変化しませんのでPOSTの扱い/reload/bookmarkに注意しましょう
  • redirectの場合はクエリ文字列なしでは情報をうまく引き継げないことに注意しましょう

URIベースのsetForward

PageクラスのsetForwardメソッドを使ってforwardを実施する方法です。この方法では、RequestDispatcherを使ったforwardが実施されるため、以下のような遷移になります

  1. HogePageの処理の過程で setForward("fuga.htm") する
  2. fuga.htm はRequestDispatcherで処理され、結果FugaPageがコールされる
  3. FugaPageで後続の処理を実施すると、fuga.htmがレンダリングされる

もちろん、上記の例はclick.xml, web.xmlなどの設定に依存しますのでほんの一例です。ここで注意してほしいのは

  • HogePage内でaddModel() addControl()したものは破棄されている
  • FugaPageでaddControl()したコントロール達は onProcess()が処理されない
    • なぜなら、Clickフレームワークで混乱防止のためにわざとそうしている(らしい)
    • onProcess()はコントロールがクリックされた場合などの判定のために使われています。ということは新しいPageがコントロールを持っているとしても、そのコントロールがイベントを処理してはいけない(しないほうがいい)ことになります。HogePageとFugaPageに同じ名前のコントロールがあるとして、HogePageでイベントハンドリングして、FugaPageにforwardしたところ、FugaPageでもクリックしたと誤認してしまう可能性があるからです。

ということです。

では、どうやってPage間で情報を受け渡しするかというと、requestのattribtueを使います。HttpServletRequestを取り出してsetAttributeすればいいわけです。ヘルパーメソッドとして

 getContext().setRequestAttribute("customer", customer);

こんな風にPageクラス内でかけます。

例は以下の通りです。

 public boolean onEditLinkClicked() {
  Customer customer = customerDao.findById(_editLink.getValueInteger() );
  getContext().setRequestAttribute("customer", customer);  
  setForward("hoge.htm");

  return false;
 }
  • return falseによって後続の処理を中断します
  • 2つのPage間での情報の受け渡しは requestのattributeです

PageインスタンスでのsetForward

URIベースでディスパッチする方法以外に、Pageクラスを明示的に指定してフォワード先を指定する方法もあります。この場合、以下の恩恵があります

  • 型付けの恩恵を受けることができる
    • たとえばプロパティにsetしてからforwardできる
    • attribute経由とかにならないのでやや洗練/再利用性が高まる

もちろん、Pageクラス自体はgetContext()などを介してClickフレームワークに強く依存していますから、あまり意味がないといえばないのですが、Pageインスタンスにプロパティを設定して、あとはよろしく、という流れは割と自然なので、好き好きで使い分けるといいでしょう。

forward元で addModel, addControl しても破壊されるのは同じですのでこの点も注意してください。

例は以下の通りです。

 public boolean onEditLinkClicked() {
  HogePage hogePage = (HogePage) getContext().createPage(HogePage.class);
  hogePage.setFuga( _editLink.getValueInteger() );
  setForward(hogePage);

  return false;
 }
  • HogePageに対してtype-safeなソースです (castしてますけどね)
  • setFuga()のような渡し方が URIベース+attributeベースのパッシングと異なります
  • return falseによって後続の処理を中断します

setRedirectでリダイレクト(30x)

いわゆるリダイレクトがこちらの方法です。request#sendRedirect()同等ですね。
これはすごくシンプルです。

 public boolean onEditLinkClicked() {
  setRedirect("/hoge.htm?param1="+_editLink.getValue());
  return false;
  • 要するにURIを生成してリダイレクトしてるだけ
  • 情報を受け渡したいならパラメータにどう入れるか、というあたりも考えないといけない
    • この方法は必須だけど、なんだか悲しい
  • return falseで後続処理を中断するのは同じ

ビュー(HTML)だけ差し替える

HogePage→hoge.htmlのようなPage:HTMLの関係があるとき、HTMLファイル、つまりテンプレートだけを違うものにしたい場合があります。この場合は

 setPath("anotherView.html");

とするだけです。これはforwardでもredirectでもなく、フレームワーク(コントローラ)に対する指示だけですので、Pageインスタンスが変わることがありません。そのため、addModel(),addControl()などはそのまま活きます。

この方法はビュー層だけを置換する機能を持たせるときには有用です。