Working with Data Components

0
130
14 min read

(For more resources related to this topic, see here.)

Introducing the DataList component

The DataList component displays a collection of data in the list layout with several display types and supports AJAX pagination. The DataList component iterates through a collection of data and renders its child components for each item.

Let us see how to use <p:dataList>to display a list of tag names as an unordered list:

<p:dataList value="#{tagController.tags}" var="tag" type="unordered" itemType="disc"> #{tag.label} </p:dataList>

The preceding <p:dataList> component displays tag names as an unordered list of elements marked with disc type bullets. The valid type options are unordered, ordered, definition, and none.

We can use type=”unordered” to display items as an unordered collection along with various itemType options such as disc, circle, and square. By default, type is set to unordered and itemType is set to disc.

We can set type=”ordered” to display items as an ordered list with various itemType options such as decimal, A, a, and i representing numbers, uppercase letters, lowercase letters, and roman numbers respectively.

Time for action – displaying unordered and ordered data using DataList

Let us see how to display tag names as unordered and ordered lists with various itemType options.

  1. Create <p:dataList> components to display items as unordered and ordered lists using the following code:

    <h:form> <p:panel header="Unordered DataList"> <h:panelGrid columns="3"> <h:outputText value="Disc"/> <h:outputText value="Circle" /> <h:outputText value="Square" /> <p:dataList value="#{tagController.tags}" var="tag" itemType="disc"> #{tag.label} </p:dataList> <p:dataList value="#{tagController.tags}" var="tag" itemType="circle"> #{tag.label} </p:dataList> <p:dataList value="#{tagController.tags}" var="tag" itemType="square"> #{tag.label} </p:dataList> </h:panelGrid> </p:panel> <p:panel header="Ordered DataList"> <h:panelGrid columns="4"> <h:outputText value="Number"/> <h:outputText value="Uppercase Letter" /> <h:outputText value="Lowercase Letter" /> <h:outputText value="Roman Letter" /> <p:dataList value="#{tagController.tags}" var="tag" type="ordered"> #{tag.label} </p:dataList> <p:dataList value="#{tagController.tags}" var="tag" type="ordered" itemType="A"> #{tag.label} </p:dataList> <p:dataList value="#{tagController.tags}" var="tag" type="ordered" itemType="a"> #{tag.label} </p:dataList> <p:dataList value="#{tagController.tags}" var="tag" type="ordered" itemType="i"> #{tag.label} </p:dataList> </h:panelGrid> </p:panel> </h:form>

  2. Implement the TagController.getTags() method to return a collection of tag objects:

    public class TagController { private List<Tag> tags = null; public TagController() { tags = loadTagsFromDB(); } public List<Tag> getTags() { return tags; } }

What just happened?

We have created DataList components to display tag names as an unordered list using type=”unordered” and as an ordered list using type=”ordered” with various supported itemTypes values. This is shown in the following screenshot:

Using DataList with pagination support

DataList has built-in pagination support that can be enabled by setting paginator=”true”. By enabling pagination, the various page navigation options will be displayed using the default paginator template. We can customize the paginator template to display only the desired options.

The paginator can be customized using the paginatorTemplate option that accepts the following keys of UI controls:

  • FirstPageLink
  • LastPageLink
  • PreviousPageLink
  • NextPageLink
  • PageLinks
  • CurrentPageReport
  • RowsPerPageDropdown

Note that {RowsPerPageDropdown} has its own template, and options to display is provided via the rowsPerPageTemplate attribute (for example, rowsPerPageTemplate=”5,10,15″). Also, {CurrentPageReport} has its own template defined with the currentPageReportTemplate option. You can use the {currentPage}, {totalPages}, {totalRecords}, {startRecord}, and {endRecord} keywords within the currentPageReport template. The default is “{currentPage} of {totalPages}“.

The default paginator template is “{FirstPageLink} {PreviousPageLink} {PageLinks} {NextPageLink} {LastPageLink}“.

We can customize the paginator template to display only the desired options.

For example: {CurrentPageReport} {FirstPageLink} {PreviousPageLink} {PageLinks}
{NextPageLink} {LastPageLink} {RowsPerPageDropdown}

The paginator can be positioned using the paginatorPosition attribute in three different locations: top, bottom, or both(default).

The DataList component provides the following attributes for customization:

  • rows: This is the number of rows to be displayed per page.
  • first: This specifies the index of the first row to be displayed. The default is 0.
  • paginator: This enables pagination. The default is false.
  • paginatorTemplate: This is the template of the paginator.
  • rowsPerPageTemplate: This is the template of the rowsPerPage dropdown.
  • currentPageReportTemplate: This is the template of the currentPageReport UI.
  • pageLinks: This specifies the maximum number of page links to display. The default value is 10.
  • paginatorAlwaysVisible: This defines if paginator should be hidden when the total data count is less than the number of rows per page. The default is true.
  • rowIndexVar: This specifies the name of the iterator to refer to for each row index.
  • varStatus: This specifies the name of the exported request scoped variable to represent the state of the iteration same as in <ui:repeat> attribute varStatus.

Time for action – using DataList with pagination

Let us see how we can use the DataList component’s pagination support to display five tags per page.

Create a DataList component with pagination support along with custom paginatorTemplate:

<p:panel header="DataList Pagination"> <p:dataList value="#{tagController.tags}" var="tag" id="tags"
type="none" paginator="true" rows="5" paginatorTemplate="{CurrentPageReport} {FirstPageLink}
{PreviousPageLink} {PageLinks} {NextPageLink}
{LastPageLink} {RowsPerPageDropdown}" rowsPerPageTemplate="5,10,15"> <f:facet name="header"> Tags </f:facet> <h:outputText value="#{tag.id} -
#{tag.label}" style="margin-left:10px" /> <br/> </p:dataList> </p:panel>

What just happened?

We have created a DataList component along with pagination support by setting paginator=”true”. We have customized the paginator template to display additional information such as CurrentPageReport and RowsPerPageDropdown. Also, we have used the rowsPerPageTemplate attribute to specify the values for RowsPerPageDropdown. The following screenshot displays the result:

Displaying tabular data using the DataTable component

DataTable is an enhanced version of the standard DataTable that provides various additional features such as:

  • Pagination
  • Lazy loading
  • Sorting
  • Filtering
  • Row selection
  • Inline row/cell editing
  • Conditional styling
  • Expandable rows
  • Grouping and SubTable and many more

In our TechBuzz application, the administrator can view a list of users and enable/disable user accounts. First, let us see how we can display list of users using basic DataTable as follows:

<p:dataTable id="usersTbl" var="user" value="#{adminController.users}"> <f:facet name="header"> List of Users </f:facet> <p:column headerText="Id"> <h:outputText value="#{user.id}" /> </p:column> <p:column headerText="Email"> <h:outputText value="#{user.emailId}" /> </p:column> <p:column headerText="FirstName"> <h:outputText value="#{user.firstName}" /> </p:column> <p:column headerText="Disabled"> <h:outputText value="#{user.disabled}" /> </p:column> <f:facet name="footer"> Total no. of Users: #{fn:length(adminController.users)}. </f:facet> </p:dataTable>

  • The following screenshot shows us the result:

PrimeFaces 4.0 introduced the Sticky component and provides out-of-the-box support for DataTable to make the header as sticky while scrolling using the stickyHeader attribute:

<p:dataTable var="user" value="#{adminController.users}"
stickyHeader="true">
... </p:dataTable>

Using pagination support

If there are a large number of users, we may want to display users in a page-by-page style. DataTable has in-built support for pagination.

Time for action – using DataTable with pagination

Let us see how we can display five users per page using pagination.

Create a DataTable component using pagination to display five records per page, using the following code:

<p:dataTable id="usersTbl" var="user" value="#{adminController.users}" paginator="true" rows="5" paginatorTemplate="{CurrentPageReport} {FirstPageLink}
{PreviousPageLink} {PageLinks}
{NextPageLink} {LastPageLink} {RowsPerPageDropdown}"
currentPageReportTemplate="( {startRecord} - {endRecord}) of
{totalRecords} Records." rowsPerPageTemplate="5,10,15"> <p:column headerText="Id"> <h:outputText value="#{user.id}" /> </p:column> <p:column headerText="Email"> <h:outputText value="#{user.emailId}" /> </p:column> <p:column headerText="FirstName"> <h:outputText value="#{user.firstName}" /> </p:column> <p:column headerText="Disabled"> <h:outputText value="#{user.disabled}" /> </p:column> </p:dataTable>

What just happened?

We have created a DataTable component with the pagination feature to display five rows per page. Also, we have customized the paginator template and provided an option to change the page size dynamically using the rowsPerPageTemplate attribute.

Using columns sorting support

DataTable comes with built-in support for sorting on a single column or multiple columns. You can define a column as sortable using the sortBy attribute as follows:

<p:column headerText="FirstName" sortBy="#{user.firstName}"> <h:outputText value="#{user.firstName}" /> </p:column>

You can specify the default sort column and sort order using the sortBy and sortOrder attributes on the <p:dataTable> element:

<p:dataTable id="usersTbl2" var="user" value="#{adminController.users}"
sortBy="#{user.firstName}" sortOrder="descending"> </p:dataTable>

The <p:dataTable> component’s default sorting algorithm uses a Java comparator, you can use your own customized sort method as well:

<p:column headerText="FirstName" sortBy="#{user.firstName}" sortFunction=
"#{adminController.sortByFirstName}
"> <h:outputText value="#{user.firstName}" /> </p:column> public int sortByFirstName(Object firstName1, Object firstName2) { //return -1, 0 , 1 if firstName1 is less than, equal to
or greater than firstName2 respectively return ((String)firstName1).compareToIgnoreCase(((String)firstName2)); }

By default, DataTable’s sortMode is set to single, to enable sorting on multiple columns set sortMode to multiple. In multicolumns’ sort mode, you can click on a column while the metakey (Ctrl or command) adds the column to the order group:

<p:dataTable id="usersTbl" var="user"
value="#{adminController.users}" sortMode="multiple"> </p:dataTable>

Using column filtering support

DataTable provides support for column-level filtering as well as global filtering (on all columns) and provides an option to hold the list of filtered records. In addition to the default match mode startsWith, we can use various other match modes such as endsWith, exact, and contains.

Time for action – using DataTable with filtering

Let us see how we can use filters with users’ DataTable.

  1. Create a DataTable component and apply column-level filters and a global filter to apply filter on all columns:

    <p:dataTable widgetVar="userTable" var="user" value=
    "#{adminController.users}" filteredValue="#{adminController.filteredUsers}" emptyMessage="No Users found for the given Filters"> <f:facet name="header"> <p:outputPanel> <h:outputText value="Search all Columns:" /> <p:inputText id="globalFilter"
    onkeyup="userTable.filter()" style="width:150px" /> </p:outputPanel> </f:facet> <p:column headerText="Id"> <h:outputText value="#{user.id}" /> </p:column> <p:column headerText="Email" filterBy="#{user.emailId}" footerText="contains" filterMatchMode="contains"> <h:outputText value="#{user.emailId}" /> </p:column> <p:column headerText="FirstName" filterBy="#{user.firstName}"
    footerText="startsWith"> <h:outputText value="#{user.firstName}" /> </p:column> <p:column headerText="LastName" filterBy="#{user.lastName}" filterMatchMode="endsWith" footerText="endsWith"> <h:outputText value="#{user.lastName}" /> </p:column> <p:column headerText="Disabled" filterBy="#{user.disabled}" filterOptions="#{adminController.userStatusOptions}" filterMatchMode="exact" footerText="exact"> <h:outputText value="#{user.disabled}" /> </p:column> </p:dataTable>

  2. Initialize userStatusOptions in AdminController ManagedBean.

    @ManagedBean @ViewScoped public class AdminController { private List<User> users = null; private List<User> filteredUsers = null; private SelectItem[] userStatusOptions; public AdminController() { users = loadAllUsersFromDB(); this.userStatusOptions = new SelectItem[3]; this.userStatusOptions[0] = new SelectItem("", "Select"); this.userStatusOptions[1] = new SelectItem("true", "True"); this.userStatusOptions[2] = new SelectItem("false", "False"); } //setters and getters }

What just happened?

We have used various filterMatchMode instances, such as startsWith, endsWith, and contains, while applying column-level filters. We have used the filterOptions attribute to specify the predefined filter values, which is displayed as a select drop-down list. As we have specified filteredValue=”#{adminController.filteredUsers}“, once the filters are applied the filtered users list will be populated into the filteredUsers property. This following is the resultant screenshot:

Since PrimeFaces Version 4.0, we can specify the sortBy and filterBy properties as sortBy=”emailId” and filterBy=”emailId” instead of sortBy=”#{user.emailId}” and filterBy=”#{user.emailId}”.

A couple of important tips

  • It is suggested to use a scope longer than the request such as the view scope to keep the filteredValue attribute so that the filtered list is still accessible after filtering.
  • The filter located at the header is a global one applying on all fields; this is implemented by calling the client-side API method called filter(). The important part is to specify the ID of the input text as globalFilter, which is a reserved identifier for DataTable.

Selecting DataTable rows

Selecting one or more rows from a table and performing operations such as editing or deleting them is a very common requirement. The DataTable component provides several ways to select a row(s).

Selecting single row

We can use a PrimeFaces’ Command component, such as commandButton or commandLink, and bind the selected row to a server-side property using <f:setPropertyActionListener>, shown as follows:

<p:dataTable id="usersTbl" var="user" value="#{adminController.users}"> <!-- Column definitions --> <p:column style="width:20px;"> <p:commandButton id="selectButton" update=":form:userDetails"
icon="ui-icon-search" title="View">

<f:setPropertyActionListener value="#{user}"
target="#{adminController.selectedUser}" />
</p:commandButton> </p:column> </p:dataTable> <h:panelGrid id="userDetails" columns="2" > <h:outputText value="Id:" /> <h:outputText value="#{adminController.selectedUser.id}"/> <h:outputText value="Email:" /> <h:outputText value="#{adminController.selectedUser.emailId}"/> </h:panelGrid>

Selecting rows using a row click

Instead of having a separate button to trigger binding of a selected row to a server-side property, PrimeFaces provides another simpler way to bind the selected row by using selectionMode, selection, and rowKey attributes. Also, we can use the rowSelect and rowUnselect events to update other components based on the selected row, shown as follows:

<p:dataTable var="user" value="#{adminController.users}" selectionMode="single" selection=
"#{adminController.selectedUser}" rowKey="#{user.id}"> <p:ajax event="rowSelect" listener=
"#{adminController.onRowSelect}" update=":form:userDetails"/> <p:ajax event="rowUnselect" listener=
"#{adminController.onRowUnselect}" update=":form:userDetails"/>
<!-- Column definitions --> </p:dataTable> <h:panelGrid id="userDetails" columns="2" > <h:outputText value="Id:" /> <h:outputText value="#{adminController.selectedUser.id}"/> <h:outputText value="Email:" />
<h:outputText value="#{adminController.selectedUser.emailId}"/> </h:panelGrid>

Similarly, we can select multiple rows using selectionMode=”multiple” and bind the selection attribute to an array or list of user objects:

<p:dataTable var="user" value="#{adminController.users}" selectionMode="multiple" selection=
"#{adminController.selectedUsers}" rowKey="#{user.id}"
> <!-- Column definitions -->
</p:dataTable>

rowKey should be a unique identifier from your data model and should be used by DataTable to find the selected rows. You can either define this key by using the rowKey attribute or by binding a data model that implements org.primefaces.model.SelectableDataModel.

When the multiple selection mode is enabled, we need to hold the Ctrl or command key and click on the rows to select multiple rows. If we don’t hold on to the Ctrl or command key and click on a row and the previous selection will be cleared with only the last clicked row selected. We can customize this behavior using the rowSelectMode attribute. If you set rowSelectMode=”add”, when you click on a row, it will keep the previous selection and add the current selected row even though you don’t hold the Ctrl or command key. The default rowSelectMode value is new. We can disable the row selection feature by setting disabledSelection=”true”.

Selecting rows using a radio button / checkbox

Another very common scenario is having a radio button or checkbox for each row, and the user can select one or more rows and then perform actions such as edit or delete.

The DataTable component provides a radio-button-based single row selection using a nested <p:column> element with selectionMode=”single”:

<p:dataTable var="user" value="#{adminController.users}"
selection="#{adminController.selectedUser}" rowKey="#{user.id}"> <p:column selectionMode="single"/> <!-- Column definitions --> </p:dataTable>

The DataTable component also provides checkbox-based multiple row selection using a nested <p:column> element with selectionMode=”multiple”:

<p:dataTable var="user" value="#{adminController.users}"
selection="#{adminController.selectedUsers}" rowKey="#{user.id}"> <p:column selectionMode="multiple"/> <!-- Column definitions --> </p:dataTable>

In our TechBuzz application, the administrator would like to have a facility to be able to select multiple users and disable them at one go. Let us see how we can implement this using the checkbox-based multiple rows selection.


Subscribe to the weekly Packt Hub newsletter

* indicates required

LEAVE A REPLY

Please enter your comment!
Please enter your name here