さて、今私は解決策を見つけました。
まず第一に、解決策は Primefaces の微調整を意味します。実際、DataTable 内に含まれる「column」タグに属性「colspan」を追加できるようにする必要があります。
これを可能にするには、「org.primefaces.component.datatable」パッケージを作成し、実際の「DataTableBeanRenderer」を拡張する「MyDataTableBeanRenderer」という名前のクラスを作成する必要があります。
このクラスでは、DataTable の本体セルのレンダリング (encodeRegularCell)、行拡張のレンダリング、およびページネーターのレンダリング (使用する場合) を処理するメソッドを「オーバーライド」する必要があります。
また、ヘッダー列グループの 2 行目に含まれる行数を取得するメソッドを追加する必要があります。dataTable のこの 2 番目のレベルに 2 番目よりも多くの行が含まれている場合、現在の "td" または "th" colspan (メソッドに応じて) が調整されます。各メソッドで、「getMaximumNumberOfColumns」の場所を確認します
さて、対応するJavaコードは次のとおりです。
@Override
protected void encodeRegularCell(FacesContext context, DataTable table, Column column, String clientId,
boolean selected) throws IOException {
final ResponseWriter writer = context.getResponseWriter();
final boolean selectionEnabled = column.getSelectionMode() != null;
final String style = column.getStyle();
String styleClass = "";
if (selectionEnabled) {
styleClass = DataTable.SELECTION_COLUMN_CLASS;
} else if (column.getCellEditor() != null) {
styleClass = DataTable.EDITABLE_COLUMN_CLASS;
}
styleClass = column.getStyleClass() == null ? styleClass : styleClass + " " + column.getStyleClass();
writer.startElement("td", null);
writer.writeAttribute("role", "gridcell", null);
if (column.getColspan() != 1) {
writer.writeAttribute("colspan", column.getColspan(), null);
}
if (style != null) {
writer.writeAttribute("style", style, null);
}
if (!isValueBlank(styleClass)) {
writer.writeAttribute("class", styleClass.trim(), null);
}
if (selectionEnabled) {
writer.startElement("div", null);
writer.writeAttribute("class", DataTable.COLUMN_CONTENT_WRAPPER, null);
encodeColumnSelection(context, table, clientId, column, selected);
writer.endElement("div");
} else {
writer.startElement("div", null);
writer.writeAttribute("class", DataTable.COLUMN_CONTENT_WRAPPER, null);
column.encodeAll(context);
writer.endElement("div");
}
writer.endElement("td");
}
@Override
protected void encodePaginatorMarkup(FacesContext context, DataTable table, String position, String tag,
String styleClass) throws IOException {
final ResponseWriter writer = context.getResponseWriter();
if (!table.isPaginatorAlwaysVisible() && table.getPageCount() <= 1) {
return;
}
final String id = table.getClientId(context) + "_paginator_" + position;
writer.startElement("tr", null);
writer.startElement(tag, null);
writer.writeAttribute("id", id, null);
writer.writeAttribute("class", styleClass, null);
final Integer rowsCount = getMaximumNumberOfColumns(table);
if (rowsCount == null || (rowsCount != null && rowsCount < table.getColumnsCount())) {
writer.writeAttribute("colspan", table.getColumnsCount(), null);
} else {
writer.writeAttribute("colspan", rowsCount, null);
}
final String[] elements = table.getPaginatorTemplate().split(" ");
for (final String element : elements) {
final PaginatorElementRenderer renderer = paginatorElements.get(element);
if (renderer != null) {
renderer.render(context, table);
} else {
writer.write(element + " ");
}
}
writer.endElement(tag);
writer.endElement("tr");
}
@Override
protected void encodeRowExpansion(FacesContext context, DataTable table) throws IOException {
final ResponseWriter writer = context.getResponseWriter();
final Map<String, String> params = context.getExternalContext().getRequestParameterMap();
final int expandedRowIndex = Integer.parseInt(params.get(table.getClientId(context) + "_expandedRowIndex"));
final String rowIndexVar = table.getRowIndexVar();
table.setRowIndex(expandedRowIndex);
if (rowIndexVar != null) {
context.getExternalContext().getRequestMap().put(rowIndexVar, expandedRowIndex);
}
writer.startElement("tr", null);
writer.writeAttribute("style", "display:none", null);
writer.writeAttribute("class", DataTable.EXPANDED_ROW_CONTENT_CLASS + " ui-widget-content", null);
writer.startElement("td", null);
final Integer rowsCount = getMaximumNumberOfColumns(table);
// Fix the number of columns of the second level with its appropriate
// number of rows
if (rowsCount == null || (rowsCount != null && rowsCount < table.getColumnsCount())) {
writer.writeAttribute("colspan", table.getColumnsCount(), null);
} else {
writer.writeAttribute("colspan", rowsCount, null);
}
table.getRowExpansion().encodeAll(context);
writer.endElement("td");
writer.endElement("tr");
table.setRowIndex(-1);
}
// get the maximum number of rows if the datatable can be expanded
private Integer getMaximumNumberOfColumns(DataTable table) {
Integer count = null;
// If the datatable contains a column group it means it can be expanded
// and contains 2 levels
if (table.getChildren().get(0) != null && table.getChildren().get(0) instanceof ColumnGroup) {
final ColumnGroup columnGroup = (ColumnGroup) table.getChildren().get(0);
if (columnGroup.getChildren().get(1) != null && columnGroup.getChildren().get(1) instanceof Row) {
final Row row = (Row) columnGroup.getChildren().get(1);
if (row.getChildren() != null) {
count = row.getChildren().size();
}
}
}
return count;
}
そして、これが私のビューに含まれる「dataTable」部分で、オーバーライドされた関数によって処理される動作に適合します。
<p:columnGroup type="header">
<p:row>
<p:column >
</p:column>
<p:column headerText="#{bundle.form_name}" filterBy="#{item.name}"
sortBy="#{item.name}" colspan="3">
</p:column>
<p:column headerText="#{bundle.form_designation}"
style="width:140px;" filterBy="#{item.designation}"
sortBy="#{item.designation}" colspan="2">
</p:column>
<p:column headerText="#{bundle.form_status}"
filterBy="#{item.status}"
filterOptions="#{itemStatusListBean.options}">
</p:column>
<p:column headerText="#{bundle.form_date}"
filterBy="#{item.statusDate}" sortBy="#{item.statusDate}">
</p:column>
</p:row>
<p:row>
<p:column />
<p:column headerText="Status" />
<p:column headerText="Type" />
<p:column headerText="Marché" />
<p:column headerText="Groupe" />
<p:column headerText="Produit" />
<p:column headerText="Désignation Produit" />
<p:column headerText="EAN" />
</p:row>
</p:columnGroup>
<p:column style="width:4%">
<p:rowToggler/>
</p:column>
<p:column colspan="3">
<h:outputText value="#{item.name}" />
</p:column>
<p:column colspan="2">
<h:outputText value="#{item.designation}" />
</p:column>
<p:column>
<h:outputText value="#{item.status.label}" />
</p:column>
<p:column>
<h:outputText value="#{item.statusDate}">
<f:convertDateTime pattern="dd/MM/yyyy" timeZone="Europe/Paris" />
</h:outputText>
</p:column>
<p:rowExpansion>
<p:dataTable id="relationDataTable"
widgetVar="relationDataTable" var="relation"
value="#{relationDataTableBean.dataModel}"
rows="10">
<p:column></p:column>
<p:column><h:outputText value="#{relation.status}"/></p:column>
<p:column><h:outputText value="#{relation.type}"/></p:column>
<p:column><h:outputText value="#{relation.market}"/></p:column>
<p:column><h:outputText value="#{relation.group}"/></p:column>
<p:column><h:outputText value="#{relation.produit}"/></p:column>
<p:column><h:outputText value="#{relation.desiProduit}"/></p:column>
<p:column><h:outputText value="#{relation.ean}"/></p:column>
</p:dataTable>
</p:rowExpansion>
したがって、基本的には、2 番目のレベルからヘッダー列をパックする必要がある最初の行のヘッダー列に colspan 値を配置するだけです。
この場合、「名称」と「名称」を扱う欄を確認してください。
<p:column headerText="#{bundle.form_name}" filterBy="#{item.name}"
sortBy="#{item.name}" colspan="3">
</p:column>
<p:column headerText="#{bundle.form_designation}"
style="width:140px;" filterBy="#{item.designation}"
sortBy="#{item.designation}" colspan="2">
</p:column>
次に、同じ「colspan」属性と値を対応する列インデックスに追加しますが、dataTable 本体 (オブジェクトのデータを表示するための列) に追加します。
<p:column colspan="3">
<h:outputText value="#{item.name}" />
</p:column>
<p:column colspan="2">
<h:outputText value="#{item.designation}" />
</p:column>
私の実装では、状況によってはいくつかのデフォルトが表示される場合がありますが、ほとんどの場合は正常に機能します。いつか同じ問題が発生した場合に役立つことを願っています。