PrimeFaces in the Enterprise
by Josh Juneau
Published April 2014
Build data-driven applications for the enterprise using the PrimeFaces JavaServer Faces UI framework.
PrimeFaces, a popular JavaServer Faces (JSF) UI framework, can be used to quickly develop sophisticated applications for the enterprise or for standard websites. This article focuses on how to efficiently build data-driven applications for the enterprise using PrimeFaces.
In this article, we'll be developing an enterprise application, making use of PrimeFaces to create a user-friendly, robust experience. The application we will be developing is called AcmePools, and it is for a swimming pool installation company named Acme Pools. It is our job to develop forms to input customer, job, and pool information, as well as provide the ability to easily retrieve and update the data, as needed.
Note: You can find the source code for the AcmePools application on GitHub.
Zero Configuration for Getting Started
Traditional JSF applications are easy to create and configure. One of the biggest boons of JSF has always been easy configuration, and integrating PrimeFaces into a JSF application is no different. The only requirement is to add the PrimeFaces library to a standard JSF application—that's it. There are some minor optional configurations for utilizing additional features within PrimeFaces, such as file upload and custom templating. However, to get started right away, no additional configuration is necessary. NetBeans IDE 8 makes it even easier, with the ability to generate PrimeFaces application pages from entity classes. This article demonstrates how to generate a PrimeFaces application using the NetBeans IDE 8 features, and then progressively customize the application making it more sophisticated.
Creating the Application
For the context of this article, we'll be using a NetBeans IDE 8 Maven-based web project. To get started, generate the database tables and sequences that will be used by the AcmePools application. You can do this within NetBeans by starting the default database that comes with NetBeans: Java DB, which is Oracle's supported distribution of the Apache Derby open source database. Then, in the Services window, expand the Databases tree, right-click Java DB, and select Start Server.
For this article, all SQL code will be executed within the sample schema, which is configured and ready to go; we'll be adding only a couple of additional tables. Double-click the sample schema database connection within the Databases tree to connect to the database. Right-click the connection and select Execute Command, which will open up a SQL session with the database. Copy and paste the SQL code that is contained within the create_database.sql
file that is part of the source code for this article, and execute it using the green arrow icon in the SQL editor (see Figure 1).
Figure 1: Creating the database
Next, create a new Maven web application project (see Figure 2).
Figure 2: Creating a Maven-based web application in NetBeans
Enter AcmePools
for the project name, and choose a project location on your file system. Enter com.acme
for Group ID, enter com.acme.acmepools
for Package Name, and click Next.
In the next dialog box, select the application server of your choice. Be sure to use an application server such as GlassFish 4.0 or WildFly, which are compatible with Java EE 7.
Next, set the Java EE Version to Java EE 7. Click Finish to create the application project.
To manually add PrimeFaces as a dependency, expand the newly created project, right-click the Dependencies item and choose Add Dependency (see Figure 3).
Figure 3: Adding a PrimeFaces Maven dependency
In the Add Dependency dialog box, type PrimeFaces
into the Query field. Expand the org.primefaces item within the search results, and choose 4.0 [jar] - central or 5.0 [jar] - central, and then click Add.
Next, enable the JSF framework within the Maven web application project. This can be done by right-clicking the project, choosing Properties, and then clicking the Frameworks category and adding JavaServer Faces (Figure 4). This automatically adds the appropriate configuration to the project's web.xml
file and creates an index.xhtml
welcome file as a starting point for your JSF application.
Figure 4: Adding the JSF framework to your project
Since PrimeFaces has been added as a Maven dependency, any new XHTML files that are created will be ready to use with PrimeFaces, because the appropriate namespace will be added by default.
Populating and Capturing Data
PrimeFaces makes the development of user-input forms simple. We all know that it is pertinent to validate user input and provide appropriate notifications to the user when something goes wrong. PrimeFaces contains a number of input components to help you develop robust data-input forms. NetBeans IDE 8.0 provides support for easy development of CRUD (create, read, update, delete) applications using NetBeans. This example will demonstrate how to use this support to create the initial input forms and data tables for the AcmePools application.
To begin, create a new package within the project named com.acme.acmepools.entity
. Take advantage of the NetBeans IDE by using the Entity Classes from Database feature (see Figure 5). To do this, right-click the newly created package, select New, and then select Entity Classes from Database. Then select the appropriate data source and choose the CUSTOMER, POOL_CUSTOMER, JOB, and POOL tables.
Note: Be sure to deselect the Include Referenced Classes checkbox, because we do not wish to generate pages from the other associated database tables.
Figure 5: Creating entity classes from the database
Click Next and retain the default selections, making sure that you create a persistence unit. Click Next again, and then click Finish. Each of the database tables should now have an associated entity class.
Now that the entity classes have been created, generate the JSF views by right-clicking the project's Web Pages folder and choosing New and then JSF Pages from Entity Classes. When the dialog box opens, select each of the newly created entity classes, and then choose Next.
On the next screen, retain all of the default values with the exception of the Choose Templates option; for that option, select PrimeFaces rather than the default. Click Finish, and NetBeans will create three separate folders within the Web Pages folder, each named accordingly for their associated entity classes. Within each folder, the following JSF pages are created: Create.xhtml
, Edit.xhtml
, List.xhtml
, and View.xhtml
. NetBeans also automatically generates JSF controllers and Enterprise JavaBeans (EJB) beans for each of the entity classes, and the newly generated pages are wired up to the controllers and ready for use.
Note: At this point, take a moment to organize the code by separating the EJB session beans, controller, and entity classes into their own packages. Use NetBeans IDE to move and refactor the classes once you've generated the new package structure. The example application uses the packages com.acme.acmepools.session
, com.acme.acmepools.jsf
, and com.acme.acmepools.entity
, respectively.
At this point, you can build the application, and then you can deploy it by right-clicking the project and choosing Run. Initially, all that will be displayed is the layout that is shown in Figure 6.
Figure 6: Default index.xhtml
page
Note: It is important to note that automatic primary key generation is not enabled by default. Specify the @GeneratedValue
and @SequenceValue
annotations within your entity classes, along with a database sequence to configure automatic primary key generation.
Clicking one of the links in the index.xhtml
view navigates to the List.xhtml
view for the chosen entity.
First things first, though. Let's make the views more recognizable. Open the bundle.properties
file, which in NetBeans is located within the project's "Other Sources" -> src/main/resources -> <default package>
package. Each of the properties within the file corresponds to an entity view within the application. Change the properties accordingly so that you can recognize which view you have selected. For instance, update the ViewCustomerTitle
property to read View Customer
, and update the ListCustomerTitle
property to read List Customer
.
Although the wizard generates these forms for you, some customization is required to make them more user-friendly. For instance, if you open the PoolCustomer List.xhtml
view and then click the Create button, you'll see lists for CustomerId
and PoolId
objects. By default, the names are not recognizable, but you can alter the code to ensure recognizable values are displayed by editing the poolCustomer -> Create.xhtml
markup and changing the f:selectItem
attributes within the PrimeFaces SelectOneMenu
components to display a label. Listing 1 shows the code for enhancing these components.
<p:outputLabel value="#{bundle.CreatePoolCustomerLabel_customerId}" for="customerId" />
<p:selectOneMenu id="customerId" value="#{poolCustomerController.selected.customerId}" >
<f:selectItems value="#{customerController.itemsAvailableSelectOne}"
var="customerIdItem"
itemLabel="#{customerIdItem.name}"
itemValue="#{customerIdItem}"/>
</p:selectOneMenu>
<p:outputLabel value="#{bundle.CreatePoolCustomerLabel_poolId}" for="poolId" />
<p:selectOneMenu id="poolId" value="#{poolCustomerController.selected.poolId}" >
<f:selectItems value="#{poolController.itemsAvailableSelectOne}"
var="poolIdItem"
itemLabel="#{poolIdItem.style}"
itemValue="#{poolIdItem}"/>
</p:selectOneMenu>
Listing 1: Adding the itemLabel
attribute to <f:selectItems>
After adding the itemLabel
attributes, the lists will be more easily readable, as shown in Figure 7.
Figure 7: Custom labels for SelectOneMenu
items
You can customize other pages and components as needed, making the application easier to use.
Creating a Custom Index Page
The default index page for the NetBeans-generated PrimeFaces application is very plain. To create a more informative and useful home page, begin by applying the application template to the index.xhtml
page. To do so, add the Facelets, PrimeFaces, and JSF Core namespaces into the view, and make use of <ui:composition>
and <ui:define>
to generate a view that adheres to the template:
xmlns:p="http://primefaces.org/ui"
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
Let's also add a more useful data set to the body of the index page by adding the customer listing inside the <ui:define name="body">
section. To do so, add a PrimeFaces DataTable
component to the view. The key attributes of the p:dataTable
element for creating a simple data table are value
and var
. The value
attribute should specify a collection of data residing within the managed bean controller, which will be displayed within the table. The var
attribute is used to reference each separate record within the table. In Listing 2, var
is set to item
, and by doing so we can reference each separate table record in more detail.
<?xml version='1.0' encoding='UTF-8' ?>
<!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:ui="http://xmlns.jcp.org/jsf/facelets">
<ui:composition template="/template.xhtml">
<ui:define name="title">
<h:outputText value="Acme Pools"></h:outputText>
</ui:define>
<ui:define name="body">
<h:form id="IndexForm">
<p:panel header="Acme Pools Customer Listing">
<p:dataTable id="datalist" value="#{customerController.items}"
var="item"
paginator="true"
rowKey="#{item.customerId}"
rows="10"
rowsPerPageTemplate="10,20,30,40,50"
>
<p:column>
<f:facet name="header">
<h:outputText value="Customer ID"/>
</f:facet>
<h:outputText value="#{item.customerId}"/>
</p:column>
<p:column>
<f:facet name="header">
<h:outputText value="Customer Name"/>
</f:facet>
<h:outputText value="#{item.name}"/>
</p:column>
<p:column>
<f:facet name="header">
<h:outputText value="Customer Email"/>
</f:facet>
<h:outputText value="#{item.email}"/>
</p:column>
</p:dataTable>
</p:panel>
</h:form>
</ui:define>
</ui:composition>
</html>
Listing 2: Adding Facelets template and PrimeFaces DataTable
to index.xhtml
To inspect the code a bit further, look in the CustomerController
class, and find the items
property (see Listing 3). If you are using the NetBeans IDE, you can hold down the Command key (for Mac OS X) or the Control key (for Microsoft Windows) and click the items
reference within the #{customerController.items}
expression to be automatically redirected to the property within the controller.
public List<Customer> getItems() {
if (items == null) {
items = getFacade().findAll();
}
return items;
}
Listing 3: Managed bean property for populating the Customer
data table
After inspecting the code, you can see that a List<Customer>
is used to populate the p:dataTable
component within the view. The new index page looks like Figure 8.
Figure 8: Newly constructed index page
Displaying and Editing Records
As seen in the previous section, one of the most efficient ways to display records of data is to list each of them within a table. Have you ever worked on a web application that required you to click a row in a table in order to edit the data, which then took you to a separate edit page? Gone are the days of multiple page navigations to accommodate simple tasks. The pages that were generated for this application by default via NetBeans contain the functionality to show the content of different views as modal dialog boxes, allowing users to stay within a single page to add or change data. This is made possible using the PrimeFaces JavaScript API to bind actions to buttons, applying those actions to specified PrimeFaces components.
For instance, the Create button on the PoolCustomer List.xhtml
view is coded as follows:
<p:commandButton id="createButton" icon="ui-icon-plus"
value="#{bundle.Create}"
actionListener="#{poolCustomerController.prepareCreate}"
update=":PoolCustomerCreateForm"
oncomplete="PF('PoolCustomerCreateDialog').show()"/>
Listing 4 shows the code for the prepareCreate
method of the PoolCustomerController
class. The code generates a new PoolCustomer
object, which can then be populated by the user and inserted into the database. This method could be customized to suit your application needs.
public PoolCustomer prepareCreate() {
selected = new PoolCustomer();
initializeEmbeddableKey();
return selected;
}
Listing 4: The prepareCreate
method of the PoolCustomerController
class
The oncomplete
attribute contains a PrimeFaces JavaScript action to open the PrimeFaces component identified as PoolCustomerCreateDialog
, which is located within the PoolCustomer Create.xhtml
view. To associate an identifier with a PrimeFaces component, specify a value for the component's widgetVar
attribute. When the button is clicked, the prepareCreate
method of the PoolCustomerController
class is invoked, and the creation dialog box is displayed when the method is finished. The update
attribute specifies that the PoolCustomerCreateForm
should be asynchronously updated.
The PrimeFaces DataTable
component can be customized very easily, making it easy to develop a highly sophisticated table for displaying and updating application data. For example, the PrimeFaces DataTable
component provides various options for editing without leaving the table view. If you take a look at the List views that were generated for the AcmePools project by NetBeans, you will see that the DataTable
components have been customized to add selection listeners. For instance, Listing 5 shows the DataTable
that is included within the customer/List.xhtml
view.
<p:dataTable id="datalist" value="#{customerController.items}"
var="item" selectionMode="single"
selection="#{customerController.selected}"
paginator="true"
rowKey="#{item.customerId}"
rows="10"
rowsPerPageTemplate="10,20,30,40,50">
<p:ajax event="rowSelect" update="createButton viewButton editButton
deleteButton"/>
<p:ajax event="rowUnselect" update="createButton viewButton editButton
deleteButton"/>
...
</p:dataTable>
Listing 5: DataTable
selection
The selectionMode
attribute indicates that a single row will be selected; this could also be set to "multiple"
to indicate that many rows can be selected at once. The selection
attribute is used to specify the object within the managed bean controller to which the selected record item will be assigned. In this case, it is a Customer
object, as seen within the CustomerController
class:
private Customer selected;
...
public Customer getSelected() {
return selected;
}
PrimeFaces <p:ajax>
elements can be embedded inside other PrimeFaces components to apply asynchronous functionality. In this case, when a row is either selected or deselected, the buttons within the view are updated. In the application, if you select a row and then scroll to the bottom of the page, you have the opportunity to edit, view, or delete the record. In Figure 9, a record has been selected and then the Edit button has been chosen.
Figure 9: Edit Customer dialog box
Note: When using NetBeans IDE 8, you can see the PrimeFaces documentation for any component by using the autocompletion feature within the editor (see Figure 10).
Figure 10: NetBeans IDE's PrimeFaces code completion functionality
The NetBeans IDE wizard has generated a fully functional CRUD application, but what if your customers had hundreds of record to edit? This could take a while to do if each row had to be selected, a button had to be clicked, and then edits needed to be made. The PrimeFaces DataTable
cell editing feature makes it easier to edit the data contained within a table.
To add cell editing capability to the customer/List.xhtml
table, add the editable
and editMode
attributes to the dataTable
element, as follows:
<p:dataTable id="datalist" value="#{customerController.items}"
var="item"
selectionMode="single" selection="#{customerController.selected}"
editable="true" editMode="cell"
paginator="true"
rowKey="#{item.customerId}"
rows="10"
rowsPerPageTemplate="10,20,30,40,50">
Next, as shown in Listing 6, use the p:cellEditor
element to mark each row that you wish to make editable, and also indicate how the output is to be displayed (via f:facet name="output"
) and indicate how to treat input (via f:facet name="input"
):
<p:column>
<f:facet name="header">
<h:outputText value="#{bundle.ListCustomerTitle_addressline1}"/>
</f:facet>
<p:cellEditor>
<f:facet name="output">
<h:outputText value="#{item.addressline1}"/>
</f:facet>
<f:facet name="input">
<p:inputText value="#{item.addressline1}"/>
</f:facet>
</p:cellEditor>
</p:column>
Listing 6: Marking rows as editable
In Listing 6, the customer addressline1
column will be editable, and when the cell is selected, the field will change to a p:inputText
component, as shown in Figure 11. This makes editing within DataTable
components very easy.
Figure 11: Editable table cell
To persist the edit, you'll need to do something with the data after it is edited. To invoke an action after a cell has been edited, embed the <p:ajax>
element inside the DataTable
. The following <p:ajax>
element can be embedded inside the customer/List.xhtml DataTable
, and then the onCellEdit
method of CustomerController
will be invoked when a cell is edited.
<p:ajax event="cellEdit" listener="#{customerController.onCellEdit}" />
The onCellEdit
method can perform any action. The example in Listing 7 saves the contents of the edited table row to the database.
public void onCellEdit(CellEditEvent event) {
Object oldValue = event.getOldValue();
Object newValue = event.getNewValue();
if(!oldValue.equals(newValue)){
// Save to the database
DataTable table = (DataTable) event.getSource();
Customer customer = (Customer) table.getRowData();
ejbFacade.edit(customer);
FacesContext.getCurrentInstance().addMessage(null,
new FacesMessage(FacesMessage.SEVERITY_INFO,"Successfully Updated",
"Updated value to " + newValue));
}
}
Listing 7: Example of onCellEdit
method
Rather than using single-cell editing, it is also possible to add a bit more control to editing functionality by creating a row editor. A row editor includes both submit and cancel functionality, and it enables editing for all the editable fields of a selected row. To add row editing to a DataTable
, omit the editMode
attribute entirely, and add the following column to either the front or the end of the table:
<p:column style="width:6%">
<p:rowEditor />
</p:column>
After adding the <p:rowEditor>
element to the front of the customer/List.xhtml
table, the New Enterprises row will resemble Figure 12.
Figure 12: Row editing capability
To persist the updates, add an event listener for row edits by embedding a <p:ajax>
element into the DataTable
, as shown below. In this case, two listeners have been added: one for when a row edit is submitted and another for when a row edit is canceled.
<p:ajax event="rowEdit" listener="#{customerController.onEdit}" />
<p:ajax event="rowEditCancel" listener="#{customerController.onCancel}" />
Using the tactics demonstrated in this section, you can develop sophisticated DataTable
components for displaying and editing your enterprise data.
Providing Informative Messages
Since asynchronous editing is a valuable feature, it would also be nice to include some feedback for users, so they know whether their edits were successful. PrimeFaces makes it easy to include informative messaging. There are a couple different messaging solutions offered by PrimeFaces: growl and messages. In this article, we'll take a look at the messages component.
To make use of messages, add the <p:messages>
element to all the relevant views that are enclosed within a form. For instance, Listing 8 shows the component added to the Customer List view.
...
<h:form id="CustomerListForm">
<p:messages showDetail="true" autoUpdate="true" closable="true"/>
<br/>
...
Listing 8: Using the <p:messages>
element
As with the other components, there are several attributes that can be specified. To display a message within the component, add a FacesMessage
to the current instance of the FacesContext
. Listing 9 demonstrates how to add a message when a cell is edited within the view.
public void onCellEdit(CellEditEvent event) {
Object oldValue = event.getOldValue();
Object newValue = event.getNewValue();
if(!oldValue.equals(newValue)){
// Save to the database
DataTable table = (DataTable) event.getSource();
Customer customer = (Customer) table.getRowData();
ejbFacade.edit(customer);
FacesContext.getCurrentInstance().addMessage(null,
new FacesMessage(FacesMessage.SEVERITY_INFO,"Successfully
Updated",
"Updated value to " + newValue));
}
}
Listing 9: Adding a message to a view
After editing a cell, the message should be displayed within the view, as shown in Figure 13.
Figure 13: PrimeFaces messages component
PrimeFaces 5
PrimeFaces 5 includes significant improvements, including new DataTable
features such as frozen cells, draggable rows, and a column toggle that enables you to hide specified columns. Moreover, new components have been added to help you produce even more robust applications.
One of the most important new features is the addition of PrimeFaces mobile, which is a mobile component library based upon the jQuery Mobile framework. This feature provides the ability to include mobile views for your application pages, allowing the application to scale across devices of all sizes. The PrimeFaces mobile feature also includes performance-based features, such as lazy loading. The best part is that this feature allows mobile and desktop user interfaces to share business logic, making it easy to develop applications for the enterprise that have the ability to span all devices.
Conclusion
The JSF framework has proven to be an efficient development framework for solutions of all sizes. PrimeFaces can be included in JSF applications to significantly increase the options available for your applications. PrimeFaces includes components that provide increased functionality compared to the standard JSF component library. It also includes solutions to increase developer productivity, such as <p:ajax>
, which makes it easy to wire components to business logic using less code. This article covered only a handful of features that PrimeFaces has to offer. For more, please visit primefaces.org.
See Also
- NetBeans website
- "PrimeFaces Development with NetBeans IDE 8" video by Geertjan Wielenga on YouTube
About the Author
Josh Juneau (juneau001@gmail.com) works as an application developer, system analyst, and database administrator. He primarily develops using Java and other Java Virtual Machine (JVM) languages. Josh is a technical writer for Oracle Technology Network and Java Magazine. He coauthored The Definitive Guide to Jython and PL/SQL Recipes (both Apress 2010) and Java 7 Recipes (Apress 2011). He recently authored Java EE 7 Recipes and Introducing Java EE 7 (Apress 2013). Josh is currently authoring the upcoming Apress title Java 8 Recipes, which will be published later this year.
Join the Conversation
Join the Java community conversation on Facebook, Twitter, and the Oracle Java Blog!