'JSF Datatable - how to select row on button push in row

I would like a button in a Primefaces DataTable row to show a dialog showing more information about the object in the row. When I click anywhere in the row not in the button, the row is selected. However, when I press the button, the row is not selected. How may I make the row that the button is in the selected row?

This example from the Primefaces showcase sets selectedCar in the backing bean and displays a dialog containing data from the row on clicking a button in the row but leaves the row unselected:

<p:dataTable id="basicDT" var="car" value="#{dtSelectionView.cars1}">
    <f:facet name="header">
        Basic
    </f:facet>
    <p:column headerText="Id">
        <h:outputText value="#{car.id}" />
    </p:column>
    <p:column headerText="Year">
        <h:outputText value="#{car.year}" />
    </p:column>
    <p:column headerText="Brand">
        <h:outputText value="#{car.brand}" />
    </p:column>
    <p:column headerText="Color">
        <h:outputText value="#{car.color}" />
    </p:column>
    <p:column style="width:32px;text-align: center">
         <p:commandButton update=":form:carDetail" oncomplete="PF('carDialog').show()" icon="ui-icon-search" title="View">
            <f:setPropertyActionListener value="#{car}" target="#{dtSelectionView.selectedCar}" />
        </p:commandButton>
    </p:column>
</p:dataTable>

.. and this example from the same page selects a row in the table and the backing bean but an subsequent button click to display a dialog:

<p:dataTable id="singleDT" var="car" value="#{dtSelectionView.cars2}" selectionMode="single" selection="#{dtSelectionView.selectedCar}" rowKey="#{car.id}">
    <f:facet name="header">
        Single with Row Click
    </f:facet>
    <p:column headerText="Id">
        <h:outputText value="#{car.id}" />
    </p:column>
    <p:column headerText="Year">
        <h:outputText value="#{car.year}" />
    </p:column>
    <p:column headerText="Brand">
        <h:outputText value="#{car.brand}" />
    </p:column>
    <p:column headerText="Color">
        <h:outputText value="#{car.color}" />
    </p:column>
    <f:facet name="footer">
        <p:commandButton process="singleDT" update=":form:carDetail" icon="ui-icon-search" value="View" oncomplete="PF('carDialog').show()" />
    </f:facet>
</p:dataTable>

I'm looking for a graceful solution where you can click any of multiple buttons in a row and select the row at the same time. Here's a use case where multiple buttons are useful - the data for the row contains two richtext fields of arbitrary size which are not easily shown in the table: Example datatable



Solution 1:[1]

Use the var value of the primefaces dataTable attribute, to create a commandLink (or button) inside each row of the dataTable:

  1. If the commandLink is clicked, an actionListener is invoked and sets the rows object as the selectedElement inside the dataTableDialog bean.

  2. Once the ajax request as finished successfully, the update attribute of the commandLink forces the dialog to request the current data from the bean.

  3. Now the JavaScript code of the oncomplete attribute shows up the dialog.

    Take a look at the actionListener of the commandLink.

The rows object is stored inside member variable selectedElement. The data of this selected element is shown by the dialog.

Here you've got a nearly complete example:

<h:form id="form">
    <p:dialog widgetVar="dlg" modal="true" id="dialog">
      <h:outputText value="#{dataTableDialog.selectedElement.key} / #{dataTableDialog.selectedElement.val}" />
    </p:dialog>

    <p:dataTable
        var="cur"
        tableStyle="width: auto !important;"
        value="#{dataTableDialog.elements}">

        <p:column>
            <h:outputText value="#{cur.key}" />
        </p:column>

        <p:column>
            <h:outputText value="#{cur.val}" />
        </p:column>

        <p:column>
            <p:commandLink
                value="Read more ..."
                actionListener="#{dataTableDialog.setSelectedElement(cur)}"
                update="form:dialog"
                oncomplete="PF('dlg').show()" />
        </p:column>

    </p:dataTable>
</h:form>

The bean:

    import java.io.Serializable;
    import java.util.ArrayList;
    import java.util.List;
    import javax.annotation.PostConstruct;

    @javax.inject.Named
    @javax.enterprise.context.SessionScoped
    public class DataTableDialog implements Serializable {

    private List<Data>  elements;
    private Data        selectedElement;

      @PostConstruct
      public void init() {
        elements = new ArrayList<>();
        elements.add(new Data("Elem 1 Key", "Elem 1 Value"));
        elements.add(new Data("Elem 2 Key", "Elem 2 Value"));
      }

      public List<Data> getElements() {
        return elements;
      }

      public Data getSelectedElement() {
        return selectedElement;
      }

      public void setSelectedElement(Data selectedElement) {
        this.selectedElement = selectedElement;
      }
    }

The data class:

public class Data implements Serializable {

    private String  key, val; // +getter/+setter

    public Data(String key, String value) {
        this.key = key;
        this.value = value;
    }
}

Solution 2:[2]

Inspired by the primefaces show case for dataTable Selection:

if it is an option to ommit the button, this example opens a dialog on row click, including row selection.

  • add an ID to your DataModel
  • add the attributes selection, selectionMode and rowKey to the dataTable
  • insert <p:ajax ... /> tag inside dataTable to show the dialog on rowSelectEvent

The facelet:

<!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:ui="http://xmlns.jcp.org/jsf/facelets"
	xmlns:h="http://xmlns.jcp.org/jsf/html"
	xmlns:f="http://xmlns.jcp.org/jsf/core"
	xmlns:p="http://primefaces.org/ui">

<h:head></h:head>

<h:body>
  <h:form id="form">
    <p:dialog
      widgetVar="elementDialog" modal="true">
      <p:outputPanel id="elementDetail">
        <p:panelGrid
          columns="2"
          rendered="#{not empty bean.selectedElement}"
          columnClasses="label,value">
          <h:outputText value="Key: #{bean.selectedElement.key}" />
          <h:outputText value="Val: #{bean.selectedElement.val}" />
        </p:panelGrid>
       </p:outputPanel>
      </p:dialog>

      <p:dataTable
        var="element"
        value="#{bean.elements}"
        selection="#{bean.selectedElement}"
        selectionMode="single"
        rowKey="#{element.id}"
        tableStyle="width: auto !important;">

        <p:ajax event="rowSelect" oncomplete="PF('elementDialog').show();" />

        <p:column headerText="Key">#{element.key}"</p:column>
        <p:column headerText="Val">#{element.val}"</p:column>
      </p:dataTable>
    </h:form>
  </h:body>
</html>

<p:dataTable
  var="element"
  value="#{bean.elements}"
  selection="#{bean.selectedElement}"
  selectionMode="single"
  rowKey="#{element.id}"
  tableStyle="width: auto !important;">

  <p:ajax
    event="rowSelect"
    oncomplete="PF('elementDialog').show();" />

...
</p:dataTable>

The Data class:

public class Data implements Serializable {

	private int    id;        // + getter/setter
	private String key, val;  // + getter/setter

	public Data(int id, String key, String value) {
		super();
		this.setId(id);
		this.key = key;
		this.value = value;
	}

}

The bean:

public class Bean implements Serializable {

	private List<Data> elements;        // + getter
	private Data       selectedElement; // + getter/setter

	@PostConstruct
	public void init() {
		elements = new ArrayList<>();
		elements.add(new Data(0, "Elem 1 Key", "Elem 1 Value"));
		elements.add(new Data(1, "Elem 2 Key", "Elem 2 Value"));
	}

}

Hopefully this example leads you to archive your goal ... ;)

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2