Eclipse+JSF+JPAで作るアプリ(13)―Primefaces データテーブルの設定 前半 ナビゲータとチェックボックス
今回は、Primefacesのデータテーブルタグ(p:datatable)の設定を色々と変えて動作を確認します。
やりたいことは、
- ソートできるようにする。
- フィルタできるようにする。
- ページナビゲーターを付ける。
- テーブルのヘッダーをウィンドウに固定する。(エクセルのウィンドウ枠固定に該当する機能です)
- チェックボックスを付ける。
- 選択時のイベントハンドルを行う。
- コンテキストメニューを表示する。
- グリッドで編集できるようにする。
です。
前半の範囲を実装すると、以下の画面のようになります。
「チェックボックスを付ける」は、tagのselectionとrowKeyだけを設定すると以下のエラーが発生します。
javax.faces.FacesException: DataModel must implement org.primefaces.model.SelectableDataModel when selection is enabled.
対応は、ListDataModelを継承して、SelectableDataModelをインプリメントします。
jsf - DataModel must implement org.primefaces.model.SelectableDataModel when selection is enabled - Stack Overflow
PrimeFacesのDataTableを探る ~ チェックボックスによる複数選択 ~ - Challenge Java EE !を参考にさせていただきました。
ここにあるよう、ListDataModel
一工夫加えて、Genericとインターフェースを使って、Movie,User,LendHistoryの3つに適応するようにしています。
それでは早速見ていきましょう。
単純なデータテーブル
<p:dataTable id="userList" var="user" value="#{editUserView.users}"> <p:column> <f:facet name="header"> <p:outputLabel value="アカウント" /> </f:facet> <p:outputLabel value="#{user.account}" /> </p:column>
テーブルの表示は、以下の通りです。
ソート可能にする。
<p:column sortBy="#{user.account}">
ソート可能にするカラムにsortByを付けるだけです。
フィルタ可能にする。
<p:column sortBy="#{user.account}" filterBy="#{user.account}" filterMatchMode="contains">
filterByを付け、filterMatchModeを付けます。filterMatchModeには、containsの他、startsWith, endsWith, exact, lt, lte, equals, gt, gte, (年度が上か下かなどに使用), inがあります。
PrimeFaces ShowCaseのドキュメント159p参照。
ページナビゲーターを付ける。
<p:dataTable id="userList" var="user" value="#{editUserView.users}" paginator="true" paginatorPosition="top" rows="10" rowsPerPageTemplate="5,10,15,30,50" >
- paginator="true"し、rows="10"を指定すると、10行ずつのページナビゲーターが表示されます。
- paginatorPositionに"top"を指定することで、上にだけナビゲーターが表示されます。
- rowsPerPageTemplateは、コンボボックスで選択できる1ページ当たりの行数をカンマ区切りで指定します。
テーブルのヘッダーをウィンドウに固定する。
<p:dataTable id="userList" var="user" value="#{editUserView.users}" paginator="true" paginatorPosition="top" rows="10" rowsPerPageTemplate="5,10,15,30,50" sortMode="multiple" stickyHeader="true" >
- stickHeader="true"に設定すると、画面スクロールに付随してテーブルのヘッダーが付いてきます。
画像だと分かりにくいですが、ホバーして移動してくれます。
- sortMode="multiple"をついでに紹介しておくと、Ctrlキーを押しながらカラムを選択すると複合でソートしてくれます。
チェックボックスを付ける。
<p:dataTable id="userList" paginator="true" paginatorPosition="top" rows="10" rowsPerPageTemplate="5,10,15,30,50" sortMode="multiple" sticy="true" value="#{editUserView.userModel}" var="user" selection="#{editUserView.selectedUsers}" rowKey="#{user.id}" > <p:column selectionMode="multiple" style="width:16px;text-align:center"/>
- p:datatableタグに、value="#{<ManagedBean名>.<後ほど説明するSelectableListDataModelのフィールド名>}"selection="#{<ManagedBean名>.<選択中のリストのメンバ>}"、rowKey="#{<データテーブルの可変変数>.<IDとなるフィールド名>"を記述することで選択可能になります。
- p:columnタグの selectionMode="multiple" とすることでチェックボックスのカラムができます。
- selectionMode="single"にするとラジオボタンでの選択になります。
例によってPrimeFaces ShowCaseで詳細が記述されています。
Showcaseの実装例では、SelectableDataModelの実装が抜けているので注意してください。(2015/7現在)
SelectableListDataModelインターフェースを実装したクラスは以下の通りです。
PrimeFacesのDataTableを探る ~ チェックボックスによる複数選択 ~ - Challenge Java EE !そのままです。
package sample.yourlibrary.view; import java.util.List; import javax.faces.model.ListDataModel; import org.primefaces.model.SelectableDataModel; import sample.yourlibrary.entity.User; public class UserListDataModel extends ListDataModel<User>implements SelectableDataModel<User> { @Override public User getRowData(String key) { List<User> users = (List<User>) getWrappedData(); long id = Long.parseLong(key); for (User user : users) { if (user.getId() == id ) { return user; } } return null; } @Override public Object getRowKey(User user) { return user.getId(); } }
ManagedBeanに、以下の実装を加えるとチェックボックスで選択可能になります。
@Getter @Setter private UserListDataModel userModel; @Getter @Setter private List<User> selectedUsers; @PostConstruct public void init() { usersDataModel = new UserListDataModel(UserManager.findAll()); }
無事選択可能になりました。
Genericを使ってSelectableListDataModelを複数のエンティティに対応させる。
他のエンティティにも、チェックボックスで選択できるようにしたいのですが、
このままでは、User, Movie, LendHistoryごとに、SelectableDataModelインターフェースを実装しなくてはなりません。
public class UserListDataModel extends ListDataModel<User>implements SelectableDataModel<User> { … public class MovieListDataModel extends ListDataModel<Movie>implements SelectableDataModel<Movie> { … public class LendHistoryListDataModel extends ListDataModel<LendHistory>implements SelectableDataModel<LendHistory> { …
すべて、getIdを持っていて、ほとんど同じ実装になります。
そこで次の手順でGenericを使って実装をし直します。
- getId(),setId(long id)を持つインターフェースIdEntityを作成し、
- すべてのエンティティにimplementsさせる。
- SelectableListDataModelインターフェースを実装したIdEntityListDataModel<T>をGeneric型で作成する。
- ManagedBeanで、IdEntityListDataModel<User>などのフィールドに変更する。
1. インターフェースIdEntity.java
package sample.yourlibrary.entity; public interface IdEntity { public long getId(); public void setId(long id); }
2. すべてのEnitityにIdEntityを実装させる。getId,setId(long id)は元々持っているため実装不要です。
public class User implements IdEntity{ … public class Movie implements IdEntity{ … public class LendHistory implements IdEntity{ …
3. IdEntityListDataModel
IdEntityを継承したモデルだけが適用できるGeneric型を作成します。
IdEntityListDataModel<T extends IdEntity>
"T extends IdEntity"を指定することで、Tには、IdEntityを継承したクラスのみ指定でき、それ以外を指定するとコンパイルエラーとなります。
package sample.yourlibrary.view; import java.util.List; import javax.faces.model.ListDataModel; import org.primefaces.model.SelectableDataModel; import sample.yourlibrary.entity.IdEntity; public class IdEntityListDataModel<T extends IdEntity> extends ListDataModel<T> implements SelectableDataModel<T> { public IdEntityListDataModel(List<T> data) { super((List<T>)data); } @Override public T getRowData(String key) { List<T> entities = (List<T>) getWrappedData(); long id = Long.parseLong(key); for (T entity : entities) { if (entity.getId() == id ) { return (T) entity; } } return null; } @Override public Object getRowKey(T entity) { return entity.getId(); } }
4. ManagedBeanで、IdEntityListDataModel
EditUserView.javaで、IdEntityListDataModelに置き換えたものが下記です。
@Getter @Setter private IdEntityListDataModel<User> userModel; @Getter @Setter private List<User> selectedUsers; @PostConstruct public void init() { userModel = new IdEntityListDataModel<User>(UserManager.findAll()); }
以上が、SelectableListDataModelの実装の一工夫でした。
後半では、
- 選択時のイベントハンドルを行う。
- コンテキストメニューを表示する。
- グリッドで編集できるようにする。
を実装していきたいと思います。