Eclipse+JSF+JPAで作るアプリ(13)―Primefaces データテーブルの設定 前半 ナビゲータとチェックボックス

今回は、Primefacesのデータテーブルタグ(p:datatable)の設定を色々と変えて動作を確認します。

やりたいことは、

  • ソートできるようにする。
  • フィルタできるようにする。
  • ページナビゲーターを付ける。
  • テーブルのヘッダーをウィンドウに固定する。(エクセルのウィンドウ枠固定に該当する機能です)
  • チェックボックスを付ける。
  • 選択時のイベントハンドルを行う。
  • コンテキストメニューを表示する。
  • グリッドで編集できるようにする。

です。

前半の範囲を実装すると、以下の画面のようになります。
f:id:tshix:20150728001053p:plain

チェックボックスを付ける」は、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, 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>

テーブルの表示は、以下の通りです。
f:id:tshix:20150727225332p:plain

ソート可能にする。

	<p:column sortBy="#{user.account}">

ソート可能にするカラムにsortByを付けるだけです。
f:id:tshix:20150727225823p:plain

フィルタ可能にする。

	<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参照。
f:id:tshix:20150727231154p:plain

ページナビゲーターを付ける。

<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ページ当たりの行数をカンマ区切りで指定します。

f:id:tshix:20150727232121p:plain

テーブルのヘッダーをウィンドウに固定する。

<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"に設定すると、画面スクロールに付随してテーブルのヘッダーが付いてきます。

f:id:tshix:20150727232946p:plain
画像だと分かりにくいですが、ホバーして移動してくれます。

  • 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());
	}

無事選択可能になりました。
f:id:tshix:20150728001053p:plain

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を使って実装をし直します。

  1. getId(),setId(long id)を持つインターフェースIdEntityを作成し、
  2. すべてのエンティティにimplementsさせる。
  3. SelectableListDataModelインターフェースを実装したIdEntityListDataModel<T>をGeneric型で作成する。
  4. 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の実装の一工夫でした。

後半では、

を実装していきたいと思います。