投稿機能を付ける

メッセージのリスト表示が出来たので、次に投稿機能を付けてみましょう。

例によって、コンポーネント化します。
投稿コンポーネントはSubmitComponentとして作り、それをTestPageに貼付けてみます。

SubmitComponentの構成ファイルは以下のようになりました。

SubmitComponent.html

<form jwcid="@Form">
		<table border="0" cellspacing="2" cellpadding="2" align="center">
			<tr>
				<td align="right">タイトル</td>
				<td><input jwcid="subject" size="60" maxlength="120"/></td>
			</tr>
			<tr>
				<td align="right">お名前</td>
				<td><input type="text" jwcid="userName" size="60" maxlength="60"/></td>
			</tr>
			<tr>
				<td align="right">Mail</td>
				<td><input type="text" jwcid="mail" size="60" maxlength="60"/></td>
			</tr>
			<tr>
				<td align="right">URL</td>
				<td><input type="text" jwcid="url" size="60" maxlength="60"/></td>
			</tr>
			<tr>
				<td colspan="2" align="right"><br/>
					<textarea jwcid="content" rows="4" cols="60"/></td>
			</tr>
			<tr>
				<td colspan="2" align="right">削除用パスワード : 
				<input type="password" jwcid="userPass" size="4" maxlength="4"/>
				<input type="submit" jwcid="submitButton" value="送信"/>
				</td>
			</tr>
		</table>
</form>

前に作ったMessageComponentと似ています。
静的な文字列を表示する代わりに、フォームを表示します。

SubmitComponent.jwc

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE component-specification PUBLIC
  "-//Apache Software Foundation//Tapestry Specification 3.0//EN"
  "http://jakarta.apache.org/tapestry/dtd/Tapestry_3_0.dtd">

<component-specification class="bbs.view.SubmitComponent"
    allow-body="no"
    allow-informal-parameters="no">
	
    <property-specification name="submitMessage" type="bbs.data.Message" persistent="yes"/>

	<component id="subject" type="TextField">
		<binding name="value" expression="submitMessage.subject"/>
	</component>
	<component id="userName" type="TextField">
		<binding name="value" expression="submitMessage.userName"/>
	</component>
	<component id="userPass" type="TextField">
		<binding name="value" expression="submitMessage.userPass"/>
		<binding name="hidden" expression="true"/>
	</component>
	<component id="mail" type="TextField">
		<binding name="value" expression="submitMessage.mail"/>
	</component>
	<component id="url" type="TextField">
		<binding name="value" expression="submitMessage.url"/>
	</component>
	<component id="content" type="TextArea">
		<binding name="value" expression="submitMessage.content"/>
	</component>
	
	<component id="submitButton" type="Submit">
		<binding name="listener" expression="listeners.submitAction"/>
	</component>    
</component-specification>

これもMessageComponentに似ています。
注意する所としては、のpersistent属性が"yes"になっている事です。
このMessageオブジェクトは、最終的にサブミットされてDBに保存されるので、"yes"にする必要があります。

SubmitComponent.java

package bbs.view;

import java.util.Date;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.log4j.Level;
import org.apache.tapestry.BaseComponent;
import org.apache.tapestry.IRequestCycle;
import org.apache.tapestry.event.PageEvent;
import org.apache.tapestry.event.PageRenderListener;
import org.objectstyle.cayenne.access.DataContext;
import org.objectstyle.cayenne.conf.Configuration;

import bbs.data.Message;

/**
 * 投稿コンポーネント
 * 
 * @author toolkit
 * @version $Revision$
 */
public abstract class SubmitComponent extends BaseComponent implements PageRenderListener {
	private static final Log log = LogFactory.getLog(SubmitComponent.class);

	public abstract Message getSubmitMessage();
	public abstract void setSubmitMessage(Message submitMessage);

	public void submitAction(IRequestCycle rc) {
		Message mes = getSubmitMessage();
		Date now = new Date();
		mes.setSubmitDate(now);
		mes.setModificationDate(now);

		DataContext context = Configuration.getSharedConfiguration().getDomain().createDataContext();
		context.registerNewObject(mes);
		context.commitChanges(Level.WARN);
		TestPage nextPage = (TestPage) rc.getPage("TestPage");
		rc.activate(nextPage);
	}

	public void pageBeginRender(PageEvent arg0) {
		Message mes = new Message();
		setSubmitMessage(mes);
	}

}

MessageComponentと大きく異なる点は、PageRenderListenerをインプリメントしている事です。
なぜそのような事をするのかというと、このコンポーネントが描画される前にSubmitMessage変数がセットされている必要があるからです。
ここがセットされていないと、ページを描画する段階で

getSubmitMessage().getSubject()

などのメソッドが呼ばれてしまいます。
当然getSubmitMessage()はnullを返しますので、例外が飛んできます。

描画する前に何かするためには、PageRenderListenerをインプリメントし、pageBeginRenderメソッドを実装します。*1

submitActionメソッド内では、データベースにコミットしています。
本当はコミットする前に入力値を検証する必要がありますが、ここでは省略します〜。

このsubmitActionメソッドのコードは、これまたWebObjectsと似ています。
データベースにコミットするコードの類似性は前の記事で紹介しましたが、ページの遷移もコードが似ていますねぇ。

Tapestry

		TestPage nextPage = (TestPage) rc.getPage("TestPage");
		rc.activate(nextPage);

WebObjects

		TestPage nextPage = (TestPage) pageWithName("TestPage");
		return nextPage;

WebObjectsではページ遷移をする場合、そのページオブジェクトを返す必要があるのでreturnしていますが、Cayenne同様、TapestryもかなりWebObjectsが意識されている事が分かると思います。

ここまで出来てしまったら、SubmitComponentをTestPageに貼付けるだけです!

TestPage.htmlは以下のようにします。

TestPage.html

<html>
	<head><title>TestPage</title></head>
	<body>
		<span jwcid="submitComponent"/>
		<hr/>
		<div align="center">
   		<span jwcid="messageLoop">
			<span jwcid="messageComponent"/>
		</span>
		</div>
	</body>
</html>

さっきと違うのは、submitComponentが追加になった所です。(まさにコンポーネントを貼付けました。)
あとは見栄えの問題で、submitComonentの下に罫線を引いて、messageLoopをセンターあわせにしただけです。

当然、.pageにもコンポーネント定義を追加しなければなりません。
とはいっても

一行を追加するだけです・・・


コンパイルし直して、ページにアクセスしてみた様子が以下です。(クリックで拡大)


こんなかんじで、ぽんぽんページが出来ます。いい感じですね!

*1:この辺はWebObjectsには無い考え方で、結構悩みました・・・はたしてこの方法が良いのか悪いのかもよく分からないんですが・・・