[Subsonic Airlines] 会員登録画面 – 入力画面の作成

今回は、入力画面について説明します。

これまでの記事
設定ファイルの作成
http://subsonic.info/ja/2015/08/23/subsonic-airlines-会員登録画面-設定ファイルの作成/

テンプレートの作成
http://subsonic.info/ja/2015/08/27/subsonic-airlines-会員登録画面-テンプレートの作成/

トップ画面の作成
http://subsonic.info/ja/2015/08/28/subsonic-airlines-会員登録画面-トップ画面の作成/

ソースコード
https://github.com/subsonicsystems/subsonic-airlines-members


/register/input.xhtml

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
      xmlns:h="http://xmlns.jcp.org/jsf/html"
      xmlns:p="http://primefaces.org/ui">
    <h:body>
        <ui:composition template="/WEB-INF/templates/default.xhtml">
            <ui:define name="content">
                <h:outputText value="会員登録"/>
                <p/>
                <h:outputText value="会員情報を入力してください(*は必須項目です)"/>
                <p/>
                <h:form id="form">
                    <p:panelGrid id="grid" columns="3" styleClass="register-input-panel-grid">
                        <p:outputLabel for="username" value="ユーザ名 *"/>
                        <p:inputText id="username" value="#{registerInputView.user.username}"
                                     styleClass="register-input-panel-grid-username-input-text"/>
                        <p:message for="username"/>
                        <p:outputLabel for="password" value="パスワード *"/>
                        <p:password id="password" value="#{registerInputView.user.password}"
                                    redisplay="true" styleClass="register-input-panel-grid-password-password"/>
                        <p:message for="password"/>
                    </p:panelGrid>
                    <div class="height30"></div>
                    <p:panelGrid columns="4" styleClass="register-input-button-panel-grid">
                        <div class="width10"></div>
                        <p:button value="戻る" icon="ui-icon-carat-1-w" outcome="/index"/>
                        <div class="width6"></div>
                        <p:commandButton value="登録" update="grid"
                                         action="#{registerInputView.save()}"/>
                    </p:panelGrid>
                </h:form>
            </ui:define>
        </ui:composition>
    </h:body>
</html>

subsonic-airlines-register-input1

トップ画面から会員登録のリンクをクリックすると、この画面を表示します。

h:formタグは、formタグを生成します。

<h:form id="form">
</h:form>

p:panelGridタグは、tableタグを生成します。

<p:panelGrid id="grid" columns="3" styleClass="register-input-panel-grid">
    <p:outputLabel for="username" value="ユーザ名 *"/>
    <p:inputText id="username" value="#{registerInputView.user.username}"
                 styleClass="register-input-panel-grid-username-input-text"/>
    <p:message for="username"/>
    <p:outputLabel for="password" value="パスワード *"/>
    <p:password id="password" value="#{registerInputView.user.password}"
                redisplay="true" styleClass="register-input-panel-grid-password-password"/>
    <p:message for="password"/>
</p:panelGrid>

columnsで列数を指定します。15行目のp:panelGridタグでは、columns=”3″としており、ラベル、インプットボックス、エラーメッセージの3列からなるテーブルを生成しています。

また、15行目のp:panelGridタグ内に6つのタグがネストされています。columns=”3″と指定しているので、最初の3つのタグが1行目となり、残りの3つのタグが2行目になります。

p:panelGridタグのstyleClassは、tableタグのclassとして生成されます。

p:outputLabelタグは、labelタグを生成します。valueには、labelタグで囲む文字列を指定します。

p:inputTextタグは、<input type=”text”>を生成します。

16行目のp:outputLabelタグのfor=”username”は、17行目のp:inputTextタグのid=”username”と対になっています。

p:inputTextタグのvalueには、<input type=”text”>の値を指定します。17行目では、EL式で#{registerInputView.user.username}と指定しています。

ここで、info.subsonic.subsonicairlines.members.view.register.InputView.javaを見てみると次のようになっています。

InputView.java

@Named("registerInputView")
@RequestScoped
public class InputView implements Serializable {
/**
 * The User.
 */
private User user;
/**
 * Gets User.
 *
 * @return the User.
 */
public User getUser() {
    return user;
}

/**
 * Sets User.
 *
 * @param user the User.
 */
public void setUser(User user) {
    this.user = user;
}

このクラスでは、@Named(“registerInputView”)が宣言されています。

@Namedは、EL式で参照する際の名称を指定します。ここでは、registerInputViewと指定しているので、InputView.javaをEL式から参照する際は、registerInputViewとなります。

また、このようにJSFのビューから参照するJavaクラスのことをバッキングビーンと呼びます。

EL式のregisterInputView.userは、registerInputViewのgetUserメソッド、またはsetUserメソッドに対応しています。ゲッターとセッターの使い分けはJSFで自動的に行われます。

さらに、registerInputView.user.usernameは、registerInputViewで保持しているUserクラスのgetUsernameメソッド、または、setUsernameメソッドに対応しています。

User.java

public class User implements Serializable {
/**
 * The username.
 */
@Column(name = "username", length = 255, unique = true)
@SuppressWarnings("checkstyle:magicnumber")
private String username;
/**
 * Gets username.
 *
 * @return the username.
 */
public String getUsername() {
    return username;
}

/**
 * Sets username.
 *
 * @param username the username.
 */
public void setUsername(String username) {
    this.username = username;
}

p:inputTextタグのstyleClassは、<input type=”text”>のclassとして生成されます。

<p:panelGrid id="grid" columns="3" styleClass="register-input-panel-grid">
    <p:outputLabel for="username" value="ユーザ名 *"/>
    <p:inputText id="username" value="#{registerInputView.user.username}"
                 styleClass="register-input-panel-grid-username-input-text"/>
    <p:message for="username"/>
    <p:outputLabel for="password" value="パスワード *"/>
    <p:password id="password" value="#{registerInputView.user.password}"
                redisplay="true" styleClass="register-input-panel-grid-password-password"/>
    <p:message for="password"/>
</p:panelGrid>

p:messageタグは、エラーメッセージを出力します。初期画面では、エラーメッセージがないので、何も表示しません。

p:messageタグのforには、対応するp:inputTextタグやp:passwordタグのidを指定します。19行目のp:messageタグのfor=”username”は、17行目のp:inputTextタグのid=”username”に対応しています。

p:passwordタグは、<input type=”password”>を生成します。

p:passwordタグのvalueとstyleClassは、p:inputTextタグと同様です。

p:passwordタグのredisplayは、入力エラーの場合の再入力時に入力したパスワードをリセットするかどうかを指定します。trueの場合、前回入力したパスワードの入力は保持され、falseの場合はリセットされます。

p:buttonタグは、<button type=”button”>を生成します。

<p:panelGrid columns="4" styleClass="register-input-button-panel-grid">
    <div class="width10"></div>
    <p:button value="戻る" icon="ui-icon-carat-1-w" outcome="/index"/>
    <div class="width6"></div>
    <p:commandButton value="登録" update="grid"
                     action="#{registerInputView.save()}"/>
</p:panelGrid>

p:buttonタグのvalueは、ボタンに表示する文字列を指定します。

p:buttonタグのiconは、ボタンに表示するアイコンを指定します。アイコン一覧は次のサイトにあります。
http://jqueryui.com/themeroller/

p:buttonタグのoutcomeは、ボタンが押された場合の遷移先を指定します。28行目では、/indexと指定しているので、/index.xhtmlへ遷移します。

p:commandButtonタグは、<button type=”submit”>を生成します。

p:commandButtonタグのvalueにボタンに表示する文字列を指定します。

p:commandButtonタグのactionは、ボタンが押された場合に実行するバッキングビーンのメソッドを指定します。メソッドは、Ajaxで実行され、XMLでレスポンスが返ってきます。

31行目において、registerInputViewのsaveメソッドを指定しています。saveメソッドでは、入力値の検証を行っています。

入力エラーの場合は、XMLを返します。XMLの中身は、部分的なHTMLで、p:commandButtonタグのupdateで指定した部分のHTMLと置き換えられます。

返ってくるXMLは、次の通りです(読みやすくするため、改行とインデントを入れています)。

<?xml version='1.0' encoding='UTF-8'?>
<partial-response id="j_id1">
    <changes>
        <update id="form:grid">
            <![CDATA[
                <table id="form:grid" class="ui-panelgrid ui-widget register-input-panel-grid" role="grid">
                    <tbody>
                        <tr class="ui-widget-content" role="row">
                            <td role="gridcell" class="ui-panelgrid-cell">
                                <label id="form:j_idt16" class="ui-outputlabel ui-widget ui-state-error" for="form:username">ユーザ名 *</label>
                            </td>
                            <td role="gridcell" class="ui-panelgrid-cell">
                                <input id="form:username" name="form:username" type="text" value="" class="ui-inputfield ui-inputtext ui-widget ui-state-default ui-corner-all ui-state-error register-input-panel-grid-username-input-text" />
                                <script id="form:username_s" type="text/javascript">
                                    PrimeFaces.cw("InputText","widget_form_username",{id:"form:username",widgetVar:"widget_form_username"});
                                </script>
                            </td>
                            <td role="gridcell" class="ui-panelgrid-cell">
                                <div id="form:j_idt17" aria-live="polite" class="ui-message ui-message-error ui-widget ui-corner-all">
                                    <span class="ui-message-error-icon"></span>
                                    <span class="ui-message-error-detail">ユーザ名を入力してください</span>
                                </div>
                            </td>
                        </tr>
                        <tr class="ui-widget-content" role="row">
                            <td role="gridcell" class="ui-panelgrid-cell">
                                <label id="form:j_idt18" class="ui-outputlabel ui-widget ui-state-error" for="form:password">パスワード *</label>
                            </td>
                            <td role="gridcell" class="ui-panelgrid-cell">
                                <input id="form:password" name="form:password" type="password" class="ui-inputfield ui-password ui-widget ui-state-default ui-corner-all ui-state-error register-input-panel-grid-password-password" />
                                <script id="form:password_s" type="text/javascript">
                                    $(function(){PrimeFaces.cw("Password","widget_form_password",{id:"form:password",widgetVar:"widget_form_password"});});
                                </script>
                            </td>
                            <td role="gridcell" class="ui-panelgrid-cell">
                                <div id="form:j_idt19" aria-live="polite" class="ui-message ui-message-error ui-widget ui-corner-all">
                                    <span class="ui-message-error-icon"></span>
                                    <span class="ui-message-error-detail">パスワードを入力してください</span>
                                </div>
                            </td>
                        </tr>
                    </tbody>
                </table>
            ]]>
        </update>
        <update id="j_id1:javax.faces.ViewState:0">
            <![CDATA[stateless]]>
        </update>
    </changes>
</partial-response>

input.xhtmlの30行目において、update=”grid”と指定しているので、<p:panelGrid id=”grid”>と</p:panelGrid>で囲まれた部分が返ってきたHTMLと置き換えられ、次のような画面となります。
subsonic-airlines-register-input2

入力エラーがなければデータベースに登録して、XMLを返します。XMLの中身は、登録完了画面のHTMLで、入力画面のHTML全体がこのHTMLに置き換えられます。

返ってくるXMLは、次の通りです(読みやすくするため、改行とインデントを入れています)。

<?xml version='1.0' encoding='UTF-8'?>
<partial-response id="j_id1">
    <changes>
        <update id="javax.faces.ViewRoot">
            <![CDATA[
                <html xmlns="http://www.w3.org/1999/xhtml">
                    <head>
                        <link type="text/css" rel="stylesheet" href="/subsonic-airlines-members/javax.faces.resource/theme.css.xhtml?ln=primefaces-bootstrap" />
                        <link type="text/css" rel="stylesheet" href="/subsonic-airlines-members/javax.faces.resource/screen.css.xhtml?ln=css" />
                        <link type="text/css" rel="stylesheet" href="/subsonic-airlines-members/javax.faces.resource/primefaces.css.xhtml?ln=primefaces&amp;v=5.2" />
                        <script type="text/javascript" src="/subsonic-airlines-members/javax.faces.resource/jquery/jquery.js.xhtml?ln=primefaces&amp;v=5.2"></script>
                        <script type="text/javascript" src="/subsonic-airlines-members/javax.faces.resource/primefaces.js.xhtml?ln=primefaces&amp;v=5.2"></script>
                        <script type="text/javascript">
                            if(window.PrimeFaces){}
                        </script>
                        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
                        <title>Subsonic Airlines</title>
                    </head>
                    <body>
                        <div>
                            <img id="j_idt10" src="/subsonic-airlines-members/javax.faces.resource/header.png.xhtml?ln=images" alt="" class="header" />
                        </div>
                        <div>
                            会員登録
                            <p></p>
                            会員登録が完了しました。
                            <p></p>
                            <a href="/subsonic-airlines-members/index.xhtml" class="ui-link ui-widget">トップページ</a>
                        </div>
                        <div class="height300"></div>
                        <div>
                            © 2015 Subsonic Airlines
                        </div>
                    </body>
                </html>
            ]]>
        </update>
        <update id="j_id1:javax.faces.ViewState:0">
            <![CDATA[stateless]]>
        </update>
    </changes>
</partial-response>

画面は次の通りです。
subsonic-airlines-register-input3

次回に続きます。