Teedaのサンプルアプリを読んでみる(7)
次はToDo一覧から[登録]で遷移するあたり。このボタンのIDは"go"で始まる"goTodoEdit"なので"doHogeHoge"みたいね対応するメソッドは無く、
- 対応するPageクラスのプロパティを更新
- Validator起動して入力値チェック
- 次画面へ遷移
となる。次画面決定のルールは、IDから"go"をとって".html"を付けたもの、ただし先頭は小文字にする。よってこの場合はtodoEdit.htmlがターゲットとなる。別アプリケーションに飛ばす場合は、パッケージ名を_(アンダースコア)で区切って指定することになるけど、詳細はJava Expert#01参照。
さてここまでTodoListPageを見てきたけど、残りはdoCreate()位かな。これTakeOverアノテーションも指定されているけど、多分どこからも呼ばれることはない。つまり機能していないと思われ。
@TakeOver(properties = "crudType") public String doCreate() { setCrudType(CrudType.CREATE); return "todoEdit"; }
ロジックから推測すると、todoEdit.htmlを新規(create)と編集(update)で共有するためどちらのモードで呼び出したかフラグをセットする、ということなんだろう。しかしtodoList.htmlの[登録]はonClickが、
document.listForm.crudType.value='0';
となっていてhidden項目のcrudTypeに'0'をセットする。一方編集はリンクが
href="todoEdit.html?fixed_crudType=2&id=id"
となっているからcrudTypeに2がセットされる("fixed_"があると右辺の値はそのままになるため)。いずれもidは"goTodoEdit"なのでtodoEdit.htmlに対応するPageクラスTodoEditPageのプロパティcrudTypeにその内容がセットされる。よって↑は不要でしょ(実際コメントにしても動作するみたいだし)。
さてTodoEditPageに行きますか。まずはinitialize()とprerender()。TodoListPageとは反対に、prerender()は実質何もしていなくて、初期化にかかわる処理はinitialize()に書かれている。そのinitialize()は
- UPDATE(CrudType=2)なら、当該レコードを取得してDTOへ変換→自画面表示
- 上記以外なら、単に自画面表示
といった感じ。(prerender()ではなく)initialize()でこれをやるのは、エラーとかで自画面を再表示するとき入力したデータが消える(というより、元のデータに戻っちゃう)のを防ぐためだよね~、なんて考えだんだけ、どうやらハズレ。この処理をそっくりprerender()へ移動させても、入力データが消えることはない。ということは、prerender()の後、ブラウザに入力された値を取得しているのか?そう考えると...。あ~そうか、画面を表示するたびにSELECT文を実行する必要ないもんね。初期表示の時だけでいいんだからinitialize()なんだ。納得(汗。
そのinitialize()で目に付くのは、編集モードのとき(CrudType=2)実行される次の1文。
getTodoDxo().convert(data, this);
このdataはjp.co.gihyo.javaexpert.todo.entity.Todoのインスタンスで、以下によって取得されたテーブルTODOの1レコード分のデータ。
Todo data = getTodoDao().selectById(getId());
TodoDao#selectById()については前に書いたけど、要は引数で渡されたidに一致するレコードを返すというもの。引数のgetId()で取得されるのは、todoList.htmlの編集リンクに埋め込まれているidの値。
href="todoEdit.html?fixed_crudType=2&id=id"
ブラウザにレンダリングされるHTMLでは、この"id=id"の左辺"id"と同じ名前のプロパティが(ForEachで使われている)todoItemsのエレメント(Todo)にあるので、右辺がその値に置換される。一方"fixed_"で始まっている場合、この書き換えは行われない。これも前に書いた。
これでdataは確定。thisは自明だからパス。問題はconvert()って何?ってこと。getTodoDxo()が返すのはインタフェースjp.co.gihyo.javaexpert.todo.web.todo.TodoDxoを実装したクラスのインスタンス。クラス名の終わりが"Dxo"なのでこれを手がかりにSMART deployを見ると
MapとJavaBeans、JavaBeansとJavaBeansといったオブジェクト間のデータの相互変換を定義するインターフェースです。インターフェースにメソッドを書いておくだけで、モデル変換のロジックは、アスペクトが自動生成します。 実装部分を作る必要はありません。
とある。なるほど。で、この部分を処理するのはS2Dxoなのね。でもこのS2DxoってSeasarプロジェクトのプロダクト一覧に載って無いんだよね~。探すのに少々手間取った(汗。
で、肝心のインタフェースはどうなっているかというと...
public interface TodoDxo { public Todo convert(AbstractTodoPage src); public void convert(Todo src, AbstractTodoPage dest); }
initialize()のconvert()は2つめの方。srcのプロパティでdestと同じ名前のものがあれば、destにsrcの値をセットしてくれる処理をS2Dxoが自動的に生成してくれる。要はこのメソッドを定義しておくだけで、テーブルTODOの検索結果をPageクラスへセットしてくれる。これ便利。だからPageにテーブルのカラム名と同じ名前のプロパティを持っていたわけね*1。