この記事では address-book アプリケーションに含まれるJakarta Faces ページファイル(xhtml ファイル)の内容について理解を進める。
index.xhtml
このファイルは address-book アプリケーションのトップページに対応する。
内容としては見出しと List.xhtml へのリンクがあるだけのページとなっている。
<h:head> のサブエレメントとして h:outputStylesheet タグが下記のように使われている。
<h:outputStylesheet library="css" name="jsfcrud.css"/>
このエレメントはレスポンスHTMLページでは下記のような link エレメントになる。
<link rel="stylesheet" href="/address-book/jakarta.faces.resource/jsfcrud.css.xhtml?ln=css">
index.xhtml 以外の他のFaces ページでも同じように h:outputStylesheet タグでCSS ファイル(jsfcrud.css)が指定されている。
List.xhtml へのリンクは下記の h:link タグで記述されている。
<p><h:link outcome="/contact/List"
value="Show All Contact Items"/></p>
この <h:link> エレメントはレスポンスHTML ページでは下記のようなアンカーエレメントとなる。
<a href="/contact/List">Show All Contact Items</a>
(この<h:link> エレメントが <h:form> のサブエレメントにされているのは単なるミスかと思われる)
template.xhtml
このファイルは他の xhtml ファイルでテンプレートとして使用されている。
テンプレートとして使用する xhtml ページ内には <ui:insert> エレメントが含まれている。この部分はテンプレート利用側で任意の内容に置き換えることができる。
複数ある場合には種類を区別するために name 属性で任意の名前を設定しておく。
テンプレートを利用する側のxhtml ファイルでは、 ui:composition タグの template 属性で、使用するテンプレートファイル名を指定する。
そして、テンプレート中の <ui:insert> エレメントを置き換える内容を <ui:define> エレメントとして定義する。
置き換え先と、対応する置き換え元は同じ name属性の値を指定する。
因みに、このアプリケーションだと template.xhtml ファイルを index.xhtml と同じように web-app ディレクトリの直下に置いている。ということは下記のURLでウェブクライアントからこのページにアクセスできる、ということを意味する。
localhost:8080/address-book/template.xhtml
(実際にブラウザで表示させた結果)
テンプレートファイルがクライアントからアクセスできないようにするには、template.xhtml ファイルを置く場所を変更して、 web-app/WEB-INF ディレクトリに置けば良い。
その場合はテンプレートを利用する側の xhtml ファイルでも下記のようにパスの指定を変更する必要がある。
<ui:composition template="/WEB-INF/template.xhtml">
List.xhtml
List ページは連絡先データが存在する場合にデータを一覧表示する。
・データがない場合は "(No Contact Items Found)" を表示
・アドレスデータが存在する場合はHTML <table> で表として表示
以下では <h:form> エレメントのサブエレメントをそれぞれ説明する。
最上部には下記のエラーメッセージ等の表示部分が含まれている。
<h:panelGroup id="messagePanel" layout="block">
<h:messages errorStyle="color: #d20005"
infoStyle="color: green"
layout="table"/>
</h:panelGroup>
layout 属性でメッセージがHTML の table タグで表示されることを指定している。
エラーの場合の色が #d20005 に、インフォメーションの場合は green にそれぞれ指定されている。
h:panelGroup タグは内側の複数のサブエレメントを1つの行としてグループ化するために使われる。(ここでは h:messages の1エレメントのみなので1列のテーブルとなる)
layout属性が "block" のときは <div> エレメントとして表示される。
この下の h:outputText エレメントではデータが存在しない場合に "(No Contact Items Found)" のテキストを出力する。
<h:outputText escape="false"
value="#{bundle.ListContactEmpty}"
rendered="#{contactController.items.rowCount == 0}"/>
ここでは value 属性でEL 表現を使って bundle.properties ファイルから ListContactEmpty key の値を取得している。
escape 属性を false に設定すると、 < などの記号を出力HTML 中でエスケープせず、そのまま表示する(true の場合は < から < に変換する)。
従って false だと文字列に <script> エレメントが含まれているとJavaScript コードがブラウザ上で実行される等のリスクがある。
rendered 属性では EL表現の条件式でこのエレメントが表示される条件を指定できる。ここでは連絡先データが0個のときのみ表示するよう指定している。
EL表現の中の contactController.items.rowCount は ContactController クラスインスタンスの items プロパティの getRowCount() メソッドの返り値を取得している。
(クラス定義等の詳細については後に続く Java ソースファイルの説明記事を参照。)
この下にはデータが存在するときのみ表示される部分が続く。
<h:panelGroup rendered="#{contactController.items.rowCount > 0}">
rendered 属性でこのエレメント全体が表示される条件をデータが存在する場合に指定している。
サブエレメントの h:outputText ではリスト表示の現在のページのデータ範囲を表示している。(ContactController.java ファイルで、1ページに10個のデータが表示されるよう設定されている)
<h:outputText
value="#{contactController.pagination.pageFirstItem + 1}..
#{contactController.pagination.pageLastItem + 1}/
#{contactController.pagination.itemsCount}"/>
前のページが存在するときには前のページへのリンクを表示する。
<h:commandLink
action="#{contactController.previous}"
value="#{bundle.Previous} #{contactController.pagination.pageSize}"
rendered="#{contactController.pagination.hasPreviousPage}"/>
次のページが存在するときに次のページへのリンクを表示する。
<h:commandLink
action="#{contactController.next}"
value="#{bundle.Next} #{contactController.pagination.pageSize}"
rendered="#{contactController.pagination.hasNextPage}"/>
連絡先データのテーブル表示には h:dataTable が使われている。
<h:dataTable value="#{contactController.items}"
var="item"
summary="Contact list"
border="0"
cellpadding="2"
cellspacing="0"
rowClasses="jsfcrud_odd_row,jsfcrud_even_row"
rules="all"
style="border:solid 1px">
このエレメントはレスポンスHTMLページでは <table> エレメントとなる。
value 属性の値にはEL 表現で ListDataModel クラスのインスタンスを指定する。
(ListDataModel クラスは jakarta.faces.model パッケージのクラス)
ここではこの contactController.items がデータベースのテーブルに保存されている全データ行を保持していると思えば良い。
var 属性の値 "item" は下に続くサブエレメントでデータの各行に対応するインスタンに参照するときの変数となる。
テーブルの各列は <h:column> エレメントで表される。
例えば Id 列は下記の h:column エレメントによって記述されている。
<h:column>
<f:facet name="header">
<h:outputText value="#{bundle.ListContactTitle_id}"/>
</f:facet>
<h:outputText value="#{item.id}"/>
</h:column>
<f:facet name="header"> エレメントはテーブルのヘッダー部分の表示に対応する。
<h:outputText value="#{item.id}"/> でデータベーステーブルの各行の id フィールド値を表示する。
続くそれぞれの <h:column> エレメントも同様にテーブルに含まれる各列の内容を順番に記述している。
このようにして items オブジェクトが持つ全ての行データが順番に最後まで出力される。
ページ最下部には "Create New Contact" と "Index" のリンクがあり、それぞれクリックにより Create.xhtml と index.xhtml へ画面遷移する。
Create.xhtml へのリンクは下記の h:commandLink エレメントで記述されている。
<h:commandLink action="#{contactController.prepareCreate}"
value="#{bundle.ListContactCreateLink}"/>
value 属性で bundle.properties ファイルから "Create New Contact" という文字列を取得している。
action 属性はこのリンクがクリックされたときに実行されるメソッドを指定している。
このメソッドの返り値である文字列 "Create" がこの action属性の値となり、これが遷移先の Create.xhtml を指している。
(HTMLとしては <a href= ... に変換される。メソッドの実行は onClick属性に設定されるJavaScript で実現される)
index.xhtml へのリンクも同様だが、action 属性では遷移先のURL を文字列で直接指定している。
<h:commandLink value="#{bundle.ListContactIndexLink}"
action="/index" immediate="true" />
h:commandLink はフォームコンポーネントなので、h:form エレメントのサブエレメントして使用する必要があることに注意。
Create.xhtml
このページでは入力フォームで連絡先データ項目を入力する。
画面下の "Save" リンクは下記の h:commandLink エレメントで記述されている。
<h:commandLink action="#{contactController.create}"
value="#{bundle.CreateContactSaveLink}" />
クリックすると、フォームのデータがサーバに送信される。同時に ContactController インスタンスの create メソッドが実行され、それによりデータベースのテーブルにその連絡先データが保存される。
詳しい仕組みを理解するには Java ソースファイルの内容を確認する必要がある。
Edit.xhtml
Edit ページは List ページで表示される各データ行の一番右の列にある "Edit" リンクから表示することができる。
Create ページと似たフォーム入力画面で、1データの編集を実行できる。
Create ページとは異なり "Save" リンクのクリック時には update メソッドが実行される。
<h:commandLink action="#{contactController.update}"
value="#{bundle.EditContactSaveLink}"/>
View.xhtml
View ページは List ページで表示される各データ行の一番右の列にある "View" リンクから表示することができる。
このページでは"View" リンクをクリックした行のデータをテーブル表示する。
下記の h:panelGrid エレメントが HTML table エレメントに変換される。
<h:panelGrid columns="2">
<h:outputText .../>
<h:outputText .../>
...
</h:panelGrid>
colmuns 属性の値はテーブルの列数を指定している。
サブエレメントのそれぞれがテーブルのセルとなるが、2列のテーブルなので h:outputText エレメント 2つずつで1行を構成する。
左側の列は下記のように bundle.properties ファイルから文字列を取得して出力している。
<h:outputText value="#{bundle.ViewContactLabel_id}"/>
右側の列は下記のようにして選択している行データに対応する各列の値を参照している。
<h:outputText value="#{contactController.selected.id}"
title="#{bundle.ViewContactTitle_id}"/>
title 属性は表示ページでマウスカーソルを合わせると表示されるツールチップのテキストを指定できる。
つまり、h:outputText エレメントは title 属性が指定されているときには、レスポンスHTMLページでは下記のような span エレメントに変換される。
<span title="Id">2</span>
次回以降の記事では Java ソースファイルやJakarta Persistence の設定についての理解を進める。Java ソースファイルの理解にはEnterprise Beans やCDI についての学習が必要になるので、まずはその辺りから情報を整理していく予定。
参考資料:
Jakarta Faces のタグライブラリのドキュメントは下記ページで参照できる。
URL: jakarta.ee/specifications/faces/4.0/vdldoc/
コメント: 記事内の情報は著者が個人的に調べた範囲で理解しているものです。必要に応じて訂正する場合があります。記事中に含まれるURL はプロトコルを含めると禁止ワードに引っ掛かるので省いています。
著者・投稿者: ENARTS05
編集履歴:
2023/10/30 作成
2023/11/2 タイトルを編集