Eclipse+JSF+JPAで作るアプリ(16)―Primefaces データテーブルの設定 後半(3) グリッド編集(cellEditor,rowEditor)
Primefacesのデータテーブルのいろいろ、ひとまずの最後はグリッド編集(セル編集)です。
こちらも、基本的に簡単です。
PrimefacesのShowcaseを見ての通り、2通りの編集方法があります。
http://www.primefaces.org/showcase/ui/data/datatable/edit.xhtml
- 鉛筆アイコンをクリックして行を変更、チェックアイコンかクローズアイコンで保存かキャンセルを選べるモード
- クリックしてセルまたは行を編集するモード。
ユーザー編集は、前者で行うこととします。
実装すると、次の画面のようになります。
このセッションでは、エンターキーへの対応がShowcaseにない注目すべき点です。
セルの編集を有効にする。
セルの編集を有効にするには、以下の流れになります。
- <p:datatable>タグの editable="true" を設定する。
- <p:datatable>のタグ内に、<p:ajax>タグで event="rowEdit", event="rowEditCancel"のイベントを追加する。
- 各カラムに対して、<p:cellEditor>タグを定義する。
- <p:cellEditor>タグ内で、<f:facet name="output">, <f:facet name="input">で編集時、参照時のコントロールを定義する。
- 鉛筆マークのアイコンを出すため、<p:rowEditor/>とだけ書かれたカラムを定義する。
具体的には、次のようにxhtmlに記述します。
1. <p:datatable>タグの editable="true" を設定する。
<p:dataTable id="userList" value="#{editUserView.userModel}" var="user" editable="true" … />
2. <p:datatable>のタグ内に、<p:ajax>タグで event="rowEdit", event="rowEditCancel"のイベントを追加する。
<p:dataTable …/> … <p:ajax event="rowEdit" listener="#{editUserView.onRowEdit}" update=":growl"/> <p:ajax event="rowEditCancel" listner="#{editUserView.onRowEditCancel}" update="growl"/> … </p:dataTable>
EditUserView.javaには、行編集のハンドラを実装します。
public void onRowEdit(RowEditEvent event) { User user = (User)event.getObject(); UserManager.updateUser(user); ViewUtil.AddMessage("ユーザの編集", "ユーザ "+ user.getName() + " を更新しました。"); } public void onRowEditCancel(RowEditEvent event) { ViewUtil.AddMessage("ユーザの編集", "編集をキャンセルしました。"); }
3. 各カラムに対して、<p:cellEditor>タグを定義する。
4. <p:cellEditorタグ内で、<f:facet name="output">, <f:facet name="input">で編集時、参照時のコントロールを定義する。
同時に記述します。
<p:column headerText="アカウント" sortBy="#{user.account}" filterBy="#{user.account}" filterMatchMode="contains"> <p:cellEditor> <f:facet name="output"><p:outputLabel value="#{user.account}" /></f:facet> <f:facet name="input"><p:inputText value="#{user.account}"/></f:facet> </p:cellEditor> </p:column>
- facetのname="output"が表示用のコントロールです。
- facetのname="input"が編集用のコントロールです。
5. 鉛筆マークのアイコンを出すため、<p:rowEditor/>とだけ書かれたカラムを定義する。
<p:column style="width:32px"> <p:rowEditor/> </p:column>
以上で、次の画面ように編集できるようになります。
Enterキーの制御
Enterキーでサブミットされるのを回避する。
このままでは、セルの編集中にEnterキーを押すとフォームがサブミットされ、以下のように削除ボタンが押されてしまいます。
これを回避するため、<h:form>タグのonkeypressアトリビュートに次のように記述します。
<h:form id="userListForm" onkeypress="if( event.keyCode == 13 ) return false;">
もっと短い書き方はこうです。
<h:form id="userListForm" onkeypress="return event.keyCode != 13;">
Enterキーでセルの編集を確定する。
また、セルの編集中、チェックアイコンをクリックしないと編集が確定されません。
これを、セルの編集中にEnterキーを押すと確定するようにするには、以下のようにxhtmlに記述します。
<h:outputScript> $(document).on("keydown", ".ui-cell-editor-input input", function(event) { if (event.keyCode == 13) { $(this).closest("tr").find(".ui-row-editor .ui-icon-check").click(); } }); </h:outputScript>
最後に、これまでのデータテーブルの設定を変えたxhtmlの全体を貼り付けておきます。
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:p="http://primefaces.org/ui" > <h2>ユーザー編集</h2> <h:outputScript> $(document).on("keydown", ".ui-cell-editor-input input", function(event) { if (event.keyCode == 13) { $(this).closest("tr").find(".ui-row-editor .ui-icon-check").click(); } }); </h:outputScript> <p:growl id="growl" showDetail="true" autoUpdate="true"/> <h:form id="addUserForm"> <p:panelGrid columns="2" > <p:outputLabel value="アカウント:" /> <p:inputText id="account" value="#{editUserView.account}" required="true" requiredMessage="アカウント名は必須です。"/> <p:outputLabel value="名前:" /> <p:inputText id="name" value="#{editUserView.name}" required="true" requiredMessage="名前は必須です。"/> <p:outputLabel value="パスワード:" /> <p:password value="#{editUserView.password}" /> <p:outputLabel value="E-mail:" /> <p:inputText id="email" value="#{editUserView.email}" /> <p:outputLabel value="管理者権限:" /> <p:selectBooleanCheckbox id="isAdmin" value="editUserView.isAdmin" style="align:center"/> </p:panelGrid> <p:messages /> <p:commandButton value="ユーザの追加" action="#{editUserView.addUser}" update=":userListForm:userList" /> </h:form> <br /> <p:separator /> <br /> <h2>ユーザー一覧</h2> <h:form id="userListForm" onkeypress="if( event.keyCode == 13 ) return false;"> <p:commandButton id="removeButton" value="選択したユーザを削除" action="#{editUserView.removeUser}" update=":userListForm" disabled="#{empty editUserView.selectedUsers}"> <p:confirm header="確認" message="選択したユーザを削除しますか?" icon="ui-icon-info" /> <p:confirmDialog global="true" showEffect="explode" hideEffect="slide"> <p:commandButton value="はい" type="button" styleClass="ui-confirmdialog-yes" icon="ui-icon-check" /> <p:commandButton value="いいえ" type="button" styleClass="ui-confirmdialog-no" icon="ui-icon-close" /> </p:confirmDialog> </p:commandButton> <br/> <br/> <p:dataTable id="userList" value="#{editUserView.userModel}" var="user" editable="true" selection="#{editUserView.selectedUsers}" rowKey="#{user.id}" paginator="true" paginatorPosition="top" rows="10" rowsPerPageTemplate="5,10,15,30,50" sortMode="multiple" stickyHeader="true"> <p:ajax event="contextMenu" update=":userListForm:removeButton" /> <p:ajax event="rowSelect" update=":userListForm:removeButton" /> <p:ajax event="rowUnselect" update=":userListForm:removeButton" /> <p:ajax event="rowSelectCheckbox" update=":userListForm:removeButton" /> <p:ajax event="rowUnselectCheckbox" update=":userListForm:removeButton" /> <p:ajax event="toggleSelect" update=":userListForm:removeButton" /> <p:ajax event="rowEdit" listener="#{editUserView.onRowEdit}" update=":growl"/> <p:ajax event="rowEditCancel" listner="#{editUserView.onRowEditCancel}" update="growl"/> <p:column selectionMode="multiple" style="width:16px;text-align:center"/> <p:column headerText="アカウント" sortBy="#{user.account}" filterBy="#{user.account}" filterMatchMode="contains"> <p:cellEditor> <f:facet name="output"><p:outputLabel value="#{user.account}" /></f:facet> <f:facet name="input"><p:inputText value="#{user.account}"/></f:facet> </p:cellEditor> </p:column> <p:column headerText="名前" sortBy="#{user.name}" filterBy="#{user.name}" filterMatchMode="contains"> <p:cellEditor> <f:facet name="output"><p:outputLabel value="#{user.name}" /></f:facet> <f:facet name="input"><p:inputText value="#{user.name}"/></f:facet> </p:cellEditor> </p:column> <p:column headerText="e-mail" sortBy="#{user.email}" filterBy="#{user.email}" filterMatchMode="contains"> <p:cellEditor> <f:facet name="output"><p:outputLabel value="#{user.email}" /></f:facet> <f:facet name="input"><p:inputText value="#{user.email}"/></f:facet> </p:cellEditor> </p:column> <p:column headerText="管理者権限" sortBy="#{user.isAdmin}" filterBy="#{user.isAdmin}" filterMatchMode="exact" style="text-align:center"> <p:cellEditor> <f:facet name="output"><p:selectBooleanCheckbox value="#{user.isAdmin}" disabled="true"/></f:facet> <f:facet name="input"><p:selectBooleanCheckbox value="#{user.isAdmin}" /></f:facet> </p:cellEditor> </p:column> <p:column style="width:32px"> <p:rowEditor/> </p:column> </p:dataTable> <p:contextMenu for="userList"> <p:menuitem value="パスワードの編集" icon="ui-icon-pencil" update="dialog" oncomplete="PF('passwordDialog').show()"> <f:setPropertyActionListener value="#{editUserView.selectedUsers[0].password}" target="#{editUserView.newPassword}"></f:setPropertyActionListener> </p:menuitem> </p:contextMenu> </h:form> <p:dialog id="dialog" header="パスワードの編集" widgetVar="passwordDialog" modal="true" showEffect="fold" hideEffect="explode" resizable="false" dynamic="true"> <h:form id="dialogForm"> <p:outputPanel id="passwordEdit" style="text-align:center;"> <p:panelGrid columns="2"> <f:facet name="header"> <p:outputLabel value="#{editUserView.selectedUsers[0].name.concat('のパスワード変更')}" /> </f:facet> <p:outputLabel value="パスワード" /> <p:password value="#{editUserView.newPassword}" redisplay="true" /> <p:outputLabel value="見えるパスワード" /> <p:inputText value="#{editUserView.newPassword}" /> </p:panelGrid> <p:commandButton value="OK" icon="ui-icon-check" action="#{editUserView.updatePassword}" oncomplete="PF('passwordDialog').hide()" /> <p:commandButton value="キャンセル" icon="ui-icon-close" oncomplete="PF('passwordDialog').hide()" /> </p:outputPanel> </h:form> </p:dialog> </html>