いろいろ備忘録日記

主に .NET とか Go とか Flutter とか Python絡みのメモを公開しています。

Tapestry奮闘記-0008 (FormコンポーネントとSubmitコンポーネントとTextFieldコンポーネント)

FormコンポーネントはFormタグの生成を担当します。また、Submitコンポーネント
サブミットボタンの生成を担当します。TextFieldコンポーネントは文字通りテキストフィールド
の生成を担当します。


今回は、フォームを使用してよくある足し算フォームを作成します。


Tapestryでのリクエスト処理は、どちらかというとGUIでのイベント処理に
似ています。コンポーネント志向のフレームワークといわれる所以ですね。


各サブミットボタンやフォームは自身が定義されているページ仕様ファイルにて
リスナーを登録されます。この登録されたリスナーがフォームのサブミット時や
ボタンのアクション時に呼ばれます。つまり、ページコンポーネントクラスの方で
好きにコールバックメソッドを作成して、後はページ仕様ファイルにてコールバック
されるよう登録すればよいのです。


今回のサンプルでは、

  • doForm001Submit
  • doCalc
  • doClear

という3つのコールバックメソッドがページコンポーネントクラス側に
定義されています。


Tapestryのイベント処理は、Swingのイベント処理に似ていて、
わかりやすいです。


[ページ仕様ファイル]

<page-specification class="gsf.samples.tapestry.Home">
    <!--
        Formコンポーネント

        HTMLのformタグの生成を担当する。
        リクエストの値を使用してページを表示したりする場合は、必須.

        Tapestryでは、フォームコンポーネントに対してSubmit時に
        起動するコールバックメソッドを指定することができる("listener"属性)
        この属性にページコンポーネントで用意したメソッドを指定すると
        フォームSubmit時にコールされるようになる。

        また、Submit時だけでなく、他のイベントの際にも(リフレッシュ時など)
        コールバックを指定できるようになっている。(Tapestryのドキュメントを参照の事)
    -->
    <component id="myForm001" type="Form">
        <!--
            フォームで使用するリクエスト送信方法
            (METHOD)

            デフォルトは、postとなっている。
        -->
        <binding name="method">literal:post</binding>

        <!--
            フォームをサブミットした際に、アクティブなセッション情報を
            取得するかどうか?

            デフォルトは、true(取得する)
        -->
        <binding name="stateful">ognl:true</binding>

        <!--
            フォームがサブミットされた際にコールされる
            コールバックメソッドを指定.

            ognlの指定の方法は、
                ognl:listeners.コールバックメソッド名

            なお、コールバックメソッドは通常、戻り値が
            いらない場合は
                public void xxxxCallBackMethod(org.apache.tapestry.IRequestCycle cycle){
                }
            という形式で作成する。
        -->
        <binding name="listener">ognl:listeners.doForm001Submit</binding>
    </component>

    <!--
        TextFieldコンポーネント

        テキストフィールドの生成を担当する.
    -->
    <component id="text001WithinForm001" type="TextField">
        <!--
            画面に表示する値を指定.
        -->
        <binding name="value">ognl:calcLeftValue</binding>
    </component>

    <component id="text002WithinForm001" type="TextField">
        <binding name="value">ognl:calcRightValue</binding>
    </component>

    <component id="answerWithinForm001" type="Insert">
        <binding name="value">ognl:calcAnswerValue</binding>
    </component>

    <!--
        Submitコンポーネント

        サブミットボタンの生成を担当する.
        このコンポーネントは、必ずFormコンポーネントの内部に
        指定しなければならない。

        Formコンポーネントと同様にこのコンポーネントも
        ボタン押下イベント時に指定したコールバックメソッドを
        指定できるようになっている。

        また、上記で指定したボタンのコールバックメソッドは
        FormコンポーネントのSubmit時にコールされるコールバックメソッド
        よりも先に起動される。
    -->
    <component id="calcButton" type="Submit">
        <!--
            ボタンが押下された際にコールされるコールバックメソッド
            を指定.
            (ここで指定したコールバックメソッドはフォームのsubmit時に
            コールされるコールバックメソッドよりも先に走る)

            ognlの指定の方法は、
                ognl:listeners.コールバックメソッド名

            なお、コールバックメソッドは通常、戻り値が
            いらない場合は
                public void xxxxCallBackMethod(org.apache.tapestry.IRequestCycle cycle){
                }
            という形式で作成する。
        -->
        <binding name="action">ognl:listeners.doCalc</binding>

        <!--
            ひとつのForm内に複数のSubmitコンポーネントが存在する場合、
            どのボタンが押下されたのかを判別するために、各ボタンに
            紐づくタグ名を保持するプロパティを指定する。

            通常、タグ名を保持するプロパティの名前は、submitTagと
            するのが慣習の模様。
        -->
        <binding name="selected">ognl:submitTag</binding>

        <!--
            実際にボタンが押下された際に上記のselected属性で
            指定されたプロパティにセットされる値。

            通常、タグ名は文字列で指定するのが慣習の模様。
        -->
        <binding name="tag">literal:calc</binding>
    </component>

    <component id="clearButton" type="Submit">
        <binding name="action">ognl:listeners.doClear</binding>
        <binding name="selected">ognl:submitTag</binding>
        <binding name="tag">literal:clear</binding>
    </component>

    <component id="currentStatus" type="Insert">
        <binding name="value">ognl:currentStatus</binding>
    </component>
</page-specification>

[テンプレートファイル]

<html jwcid="@Shell">
    <body jwcid="@Body">
        <!--
            フォームを使用したコンポーネント連携
            (Formコンポーネント, Submitコンポーネントを使用)
        -->
        <br/>
        <hr/>
        <div id="form_submit_001" align="center">
            <form jwcid="myForm001" name="form1" method="post" action="">
                <b>
                    Formコンポーネント, Submitコンポーネントを使用して、リクエストを処理する.
                    <br/>
                    <br/>
                    <font color="red">
                        ここでは、Formコンポーネントでのコールバックと、Submitコンポーネントの<br/>
                        コールバックの動作を確認するために、以下の動作としている。<br/>
                        <br/>
                        <br/>
                        <table>
                            <tr>
                                <td>
                                    <li>Submitコンポーネントのコールバックにて入力された2つの値を足す.</li>
                                </td>
                            </tr>
                            <tr>
                                <td>
                                    <li>Formコンポーネントのコールバックにて上で算出された結果に1000を足す.</li>
                                </td>
                            </tr>
                        </table>
                    </font>
                </b>
                <br/>
                <br/>
                <table border="1">
                    <tr>
                        <th colspan="4" align="center">計算</th>
                    </tr>
                    <tr>
                        <td>
                            <input jwcid="text001WithinForm001" type="text" name="textfield1" value="" size="4" maxlength="3"/>
                            +
                            <input jwcid="text002WithinForm001" type="text" name="textfield2" value="" size="4" maxlength="3"/>
                            +
                            1000
                            =
                        </td>
                        <td>
                            <span jwcid="answerWithinForm001">0</span>
                        </td>
                        <td>
                            <input jwcid="calcButton" type="submit" name="calcButton" value="計算"/>
                        </td>
                        <td>
                            <input jwcid="clearButton" type="submit" name="clearButton" value="値をクリア"/>
                        </td>
                    </tr>
                </table>
                <br/>
                現在の状況: <b><font color="red"><span jwcid="currentStatus">状況・・・</span></font></b>
            </form>
        </div>
        <br/>
        <hr/>
    </body>
</html>

[ページコンポーネントクラス]

// vim:set ts=4 sw=4 et ws ft=java fenc=cp932 ff=dos:
package gsf.samples.tapestry;

import java.util.*;
import java.text.*;

import org.apache.tapestry.*;
import org.apache.tapestry.html.*;

/**
 * Homeページ用のページコンポーネントクラス.<br/>
 * <br/>
 * ページコンポーネントクラスは、org.apache.tapestry.html.BasePage<br/>
 * クラスを継承して、作成する。tapestryには、バイトコードを実行時に<br/>
 * 拡張するフレームワークが存在するので、ページコンポーネントクラスは<br/>
 * abstractで宣言してもよい。その際は、セッターゲッターをabstractで<br/>
 * 宣言すると実行時にtapestry側で実装を拡張してくれる。<br/>
 *
 * <pre>
 * (クラス宣言例)
 * public abstract class Home extends BasePage{
 * }
 *
 * (メソッド宣言例)
 * public abstract String getName();
 * public abstract void   setName(String name);
 * </pre>
 *
 * <br/>
 * また、コンポーネントのリファレンスに
 * 型がIActionListnerとなっている場合は、
 * ページコンポーネントクラスで任意のメソッドを
 * 定義し、そのコンポーネントの属性に設定すれば
 * そのメソッドが呼ばれる。
 * (Tapestryは、実行時にlistner属性に指定されている
 * メソッドをリフレクションを使用してページクラスもしくは
 * コンポーネントクラスより探し出し、IActionListenerインターフェース
 * を実装したオブジェクトを作成してくれる。)
 * <br/>
 * <pre>
 * (コールバックメソッド宣言例)
 * [ページ仕様ファイル]
 * <component id="myForm" type="Form">
 *     <binding name="listener">ognl:listeners.callBackMethod</binding>
 * </component>   
 *
 * [ページクラス(javaファイル)]
 * public void callBackMethod(org.apache.tapestry.IRequestCycle cycle){
 *     //
 *     // process........
 *     //
 * }
 * </pre>
 */
public class Home extends BasePage{

    private Date    currentDate;
    private Format  dateFormat;
    private String  largeStrings;
    private Integer calcLeftValue;
    private Integer calcRightValue;
    private Integer calcAnswerValue;
    private String  currentStatus;

    private String  submitTag;

    public Home(){
        this.currentDate  = new Date();
        this.dateFormat   = new SimpleDateFormat("yyyy/MM/dd hh:mm:ss");
        this.largeStrings = "改行付きの文字列<>&''[0]\n改行付きの文字列<>&''[1]\n改行付きの文字列<>&''[2]\n";

        this.calcLeftValue   = 0;
        this.calcRightValue  = 0;
        this.calcAnswerValue = 0;

        this.currentStatus   = "";

        this.submitTag       = null;
    }
    
    /**
     * jwcidが"myForm001"のFormコンポーネントがSubmit<br/>
     * された際に呼ばれるコールバックメソッド.<br/>
     *
     * @param cycle tapestryリクエストサイクルオブジェクト
     *
     */
    public void doForm001Submit(IRequestCycle cycle){

        //
        // ボタンが押下された際に紐づいているタグ名が
        // submitTagにセットされている。
        // その値を用いて、どのボタンが押下されたのかを
        // 判断.
        //
        String selectedTag 
            = (this.getSubmitTag() != null) 
                        ? this.getSubmitTag().toLowerCase() 
                        : "";

        if("calc".equals(selectedTag)){
            //
            // ボタン押下のコールバックの際に計算された
            // 値に1000を足す。
            //
            this.setCalcAnswerValue(this.getCalcAnswerValue() + 1000);

            // ステータスを更新
            this.setCurrentStatus("計算を行いました。");

        }else if("clear".equals(selectedTag)){
            
            // ステータスを更新
            this.setCurrentStatus("値をクリアしました。");
        }

    }

    /**
     * jwcidが"calcButton"のSubmitコンポーネントが押下された際に<br/>
     * 呼ばれるコールバックメソッド.<br/>
     *
     * @param cycle tapestryリクエストサイクルオブジェクト
     *
     */
    public void doCalc(IRequestCycle cycle){
        
        //
        // フィールドの値を足し、答えをセット
        //
        Integer answer = this.getCalcLeftValue() + this.getCalcRightValue();

        this.setCalcAnswerValue(answer);

    }
    
    /**
     * jwcidが"clearButton"のSubmitコンポーネントが押下された<br/>
     * 際にコールされるコールバックメソッド.<br/>
     *
     * @param cycle tapestryリクエストサイクルオブジェクト
     *
     */
    public void doClear(IRequestCycle cycle){

        //
        // フィールドの値をクリア
        //
        this.setCalcLeftValue(0);
        this.setCalcRightValue(0);
        this.setCalcAnswerValue(0);
    }

    /**
     * Get currentDate.
     *
     * @return currentDate as Date.
     */
    public Date getCurrentDate(){
        return this.currentDate;
    }
    
    /**
     * Get dateFormat.
     *
     * @return dateFormat as Format.
     */
    public Format getDateFormat(){
        return this.dateFormat;
    }
    
    /**
     * Get largeStrings.
     *
     * @return largeStrings as String.
     */
    public String getLargeStrings(){
        return this.largeStrings;
    }
    
    /**
     * Get calcLeftValue.
     *
     * @return calcLeftValue as Integer.
     */
    public Integer getCalcLeftValue(){
        return this.calcLeftValue;
    }
    
    /**
     * Set calcLeftValue.
     *
     * @param calcLeftValue the value to set.
     */
    public void setCalcLeftValue(Integer calcLeftValue){
        this.calcLeftValue = calcLeftValue;
    }
    
    /**
     * Get calcRightValue.
     *
     * @return calcRightValue as Integer.
     */
    public Integer getCalcRightValue(){
        return this.calcRightValue;
    }
    
    /**
     * Set calcRightValue.
     *
     * @param calcRightValue the value to set.
     */
    public void setCalcRightValue(Integer calcRightValue){
        this.calcRightValue = calcRightValue;
    }
    
    /**
     * Get calcAnswerValue.
     *
     * @return calcAnswerValue as Integer.
     */
    public Integer getCalcAnswerValue(){
        return this.calcAnswerValue;
    }
    
    /**
     * Set calcAnswerValue.
     *
     * @param calcAnswerValue the value to set.
     */
    public void setCalcAnswerValue(Integer calcAnswerValue){
        this.calcAnswerValue = calcAnswerValue;
    }
    
    /**
     * Get submitTag.
     *
     * @return submitTag as String.
     */
    public String getSubmitTag(){
        return this.submitTag;
    }
    
    /**
     * Set submitTag.
     *
     * @param submitTag the value to set.
     */
    public void setSubmitTag(String submitTag){
        this.submitTag = submitTag;
    }
    
    /**
     * Get currentStatus.
     *
     * @return currentStatus as String.
     */
    public String getCurrentStatus(){
        return this.currentStatus;
    }
    
    /**
     * Set currentStatus.
     *
     * @param currentStatus the value to set.
     */
    public void setCurrentStatus(String currentStatus){
        this.currentStatus = currentStatus;
    }
}