読者です 読者をやめる 読者になる 読者になる

PrimeFaces の DataTable で paginator と binding を一緒に使うと…

メモ:PF DataTable でページングと binding を一緒に使った時に問題が出る。
画面イメージとか貼った方が分かりやすそうですが、面倒なんで用意してません。

バージョンは PrimeFaces 5.2 です。

コードはこんなの。

PrimeFaces の DataTable で paginator と binding を指定しています。
また、行には commandLink を置いてそこで次の画面に遷移さす感じ。

<!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:h="http://xmlns.jcp.org/jsf/html"
  xmlns:p="http://primefaces.org/ui"
  xmlns:f="http://xmlns.jcp.org/jsf/core"
  xmlns:scmui="http://jp.co.scm-net/jsf/components">
<h:head>
  <f:facet name="first">
    <meta http-equiv="X-UA-Compatible" content="IE=Edge" />
    <meta charset="UTF-8" />
  </f:facet>
  <title>Binding</title>
</h:head>
<h:body>
  <h:form id="form">
    <h1>Primefaces DataTable paginator + binding デモ</h1>
    <h2>いきなり1ページ目のリンクを押すと、何故か2度押しされた感じになる?(但し、2回目はViewScopedなBeanのpostConstructも呼ばれる)</h2>
    <p:dataTable binding="#{dataTableBean.dataTable}" paginator="true" rows="2" value="#{dataTableBean.details}" var="item">
      <p:column headerText="name"><h:outputText value="#{item.name}"/></p:column>
      <p:column headerText="link"><p:commandLink value="リンク" action="#{dataTableBean.jump}" ajax="false"/> </p:column>
    </p:dataTable> 
  </h:form>
</h:body>
</html>

Java 側は大したことないコード。
遷移先の nextView.xhtml は空でもOK。

package sample;

import java.io.Serializable;
import java.util.Arrays;
import java.util.List;
import java.util.logging.Logger;

import javax.annotation.PostConstruct;
import javax.faces.view.ViewScoped;
import javax.inject.Inject;
import javax.inject.Named;

import org.primefaces.component.datatable.DataTable;

@Named
@ViewScoped
public class DataTableBean implements Serializable {
  private static final long serialVersionUID = 1L;
  
  static final Logger logger = Logger.getLogger(DataTableBean.class.getName());
  
  private DataTable dataTable;
  public DataTable getDataTable() {
    return dataTable;
  }
  public void setDataTable(DataTable dataTable) {
    this.dataTable = dataTable;
  }
  
  private List<DetailBean> details;
  public List<DetailBean> getDetails() {
    return details;
  }
  
  @PostConstruct
  void postConstruct() {
    logger.info("DataTableBean#postConstruct : " + this.hashCode());
    details = Arrays.asList(new DetailBean("aaa"), new DetailBean("bbb"), new DetailBean("ccc"));
  }

  public String jump() {
    logger.info("DataTableBean#jump : " + this.hashCode());
    return "nextView";
  }
}

public class DetailBean implements Serializable {
  private static final long serialVersionUID = 1L;
  private String name;

  public String getName() {
    return name;
  }
  public void setName(String name) {
    this.name = name;
  }
  
  public DetailBean() {}
  public DetailBean(String name) { this.name = name; }
}

paginator と binding を使ってる以外は、ありふれた明細からリンクな感じのコードです。

で、何が起きるかというと DataTable にデータが表示された直後に 1ページ目の何れかの行の CommandLink/Button を押すと、何故か2回 action/actionListener が走ってしまいます。
action で画面遷移 + ViewScoped な Baking Bean の場合は、postConstruct も走ります。

他のページのリンクは大丈夫です。また、他のページに移動してから1ページ目に戻っても起きません。
もっというと、同じ画面に CommandLink/Button が別にあり、それをclickしてからなら1ページ目でも起きません。
要はCommandLink/Button の最初のリクエストがDataTable内のリンク/ボタンならダメな感じ。
CommandLink/Button と書いたのは、DataTable外の inputText の ajax を走らせてからでも現象が発生したからです。

ちなみに dataTable の binding を外した場合は、最初のリクエストが DataTable 内の リンク/ボタンでも大丈夫でした。
また、paginator を使っていない場合も大丈夫です。

paginator と binding 両方使ってる時だけ起きるようです。
ガッツが足りないのでソースは追っかけてませんが、ページングを使う場合はなるべく binding を使わない方が良さそうです。
そもそも binding 自体あまり使わないかもしれませんが。。
サンプルコード全体はこちら
OdaShinsuke/DataTablePaginator · GitHub

再検索した時に1ページ目に戻したいとかも、binding を使わずに FacesContext から直接取るか、
JavaScript

PF('widgetTable').getPaginator().setPage(0);

でやる方法もあります。

http://forum.primefaces.org/ にはうまく説明出来そうにないので投げてません。