PrimeFaces Cookbook - Second Edition - Sample Chapter
PrimeFaces Cookbook - Second Edition - Sample Chapter
PrimeFaces Cookbook - Second Edition - Sample Chapter
ee
and problems
$ 44.99 US
29.99 UK
P U B L I S H I N G
P U B L I S H I N G
Sa
pl
e
PrimeFaces Cookbook
Second Edition
Mert alkan
Oleg Varaksin
PrimeFaces is the most popular component library used in the JSF ecosystem. It is a lightweight library with
one JAR, zero configuration, and no required dependencies.
Mert alkan
Oleg Varaksin
Oleg Varaksin is a senior software engineer living in the Black Forest, Germany. He is a
graduate computer scientist who studied informatics at Russian and German universities.
His main occupation and "daily bread" in the last 10 years has consisted of building
various web applications based on JSP, JSF, CDI, Spring, web services, REST, jQuery,
AngularJS, and HTML5. He has a deep understanding of web usability and accessibility.
Oleg is an experienced JSF expert and has been working with the PrimeFaces library
since its beginning in 2009. He is also a well-known member of the PrimeFaces
community and a cocreator of the PrimeFaces Extensions project on additional JSF
components for PrimeFaces.
Besides the aforementioned technologies, he has worked as a frontend developer with
many other web and JavaScript frameworksStruts, GWT, Prototype, YUI library, and
so on. He also implemented an AJAX framework before all the hype about AJAX began.
Oleg normally shares the knowledge he has acquired on his blog at
.
PrimeFaces Cookbook
Second Edition
PrimeFaces Cookbook, Second Edition, is the most comprehensive book about
PrimeFacesthe rapidly evolving and leading JSF component suite. The book
provides a head start to its readers by covering all the knowledge needed to work
with the PrimeFaces framework and components in the real world. It is a quick,
practical guide to learn PrimeFaces, written in a clear, comprehensible style.
PrimeFaces Cookbook, Second Edition, addresses a wide audience interested in
modern, trendsetting Java EE web development.
Chapter 11, Miscellaneous Advanced Use Cases, introduces more interesting features
of the PrimeFaces library. You will learn about RequestContexta helpful utility
that allows marking components as updatable targets at runtime, adding AJAX
callback parameters, opening external pages in dynamically generated dialog (Dialog
Framework), and more. In this chapter, a number of real-world samples will be also
developedblocking UI during AJAX calls, periodic polling, focus handling,
controlling from submission, sticking components, content caching, and targetable
messages, to name a few. Furthermore, after reading this chapter, readers will be aware
of the pitfalls of menus within layout units and nested panels as well as possibilities
for exception handling.
Introduction
Drag and drop is an action, which means grabbing an object and dragging it to a different
location. The components capable of being dragged and dropped enrich the Web and make a
solid base for modern UI patterns. The drag and drop utilities in PrimeFaces allow us to create
draggable and droppable user interfaces efficiently. They make it abstract for the developers
to deal with the implementation details at the browser level.
In this chapter, we will learn about PrimeFaces' drag and drop utilitiesdraggable and
droppable. AJAX-enhanced drag and drop, and a special integration with data iteration
components, will be explained as well.
How to do it
A component can be made draggable by using p:draggable. The value of the for
attribute specifies a search expression for the draggable target. In our case, it matches
the ID of p:panel.
Chapter 1, Getting Started with PrimeFaces, provides more details
on search expressions.
If the for attribute is omitted, the parent component will be selected as a draggable target.
Let us make some panel components draggable and apply some basic features:
<p:panel id="pnl" header="Draggable panel with default settings">
Drag me around
</p:panel>
<p:draggable for="pnl"/>
<p:panel id="hpnl" header="Draggable panel by handle">
I can be only dragged by my header
</p:panel>
<p:draggable for="hpnl" handle=".ui-panel-titlebar"/>
<p:panel id="cpnl" header="Draggable panel with clone">
I display a clone as helper while being dragged
</p:panel>
<p:draggable for="cpnl" helper="clone"/>
<p:panel id="rpnl" header="Draggable panel with revert">
I will be returned to my start position when dragging stops
</p:panel>
<p:draggable for="rpnl" revert="true"/>
<p:panel id="opnl" header="Draggable panel with opacity">
258
Chapter 8
I use opacity for helper while being dragged
</p:panel>
<p:draggable for="opnl" opacity="0.5"/>
The following screenshot shows the five panels. The last panel is being dragged. Its opacity
has been changed to 0.5 after the dragging starts.
How it works
By default, any point in a dragged component can be used as a handle. To restrict the dragstart click to a specified element(s), we can use the handle option, which is a jQuery selector.
The second panel is dragged by using its header only.
By default, the actual component is used as a drag indicator. The helper option allows
keeping the component at its original location during dragging. This can be achieved with
helper set to clone for the third panel.
If the revert option is set to true, the component will return to its starting position when
the dragging stops, and the draggable component is not dropped onto a matching droppable
component. The fourth panel features this behavior.
Opacity for helper, while it is being dragged, is another useful option to give the user a
visual feedback. The opacity of the fifth panel is reduced when dragging.
259
There's more
Other basic features are related to the attributes cursor and stack. cursor is a CSS cursor
that is to be displayed when dragging. It is handy to set its value to move. stack is a jQuery
selector. It controls the z-index of the set of draggable elements that match the selector
and always brings them to the front. That means the draggable component always overlays
the other draggables.
See also
Refer to the Restricting dragging by axis, grid, and containment and Snapping to the edges of
nearest elements recipes discussed later in this chapter to learn the advanced features
of Draggable.
How to do it
The next example demonstrates three draggable panels and one draggable image. The first
panel can be dragged only horizontally, the second one only vertically, and the third panel
is dragged on a grid. Dragging on a grid means the dragging helper snaps to a gridevery
specific x and y pixel. The image is placed within an h:panelGroup tag, which acts as a
container for dragging. The image cannot go outside this container.
<p:panel id="hpnl" header="Only horizontal draggable panel">
I can be only dragged horizontally.
</p:panel>
260
Chapter 8
<p:draggable for="hpnl" axis="x"/>
<p:panel id="vpnl" header="Only vertical draggable panel">
I can be only dragged vertically
</p:panel>
<p:draggable for="vpnl" axis="y"/>
<p:panel id="gpnl" header="Draggable panel in grid [40,50]">
I can be only dragged in a grid
</p:panel>
<p:draggable for="gpnl" grid="40,50"/>
The image below can be only dragged within its parent's boundaries
<h:panelGroup layout="block"
styleClass="dragContainer ui-widget-content">
<h:graphicImage id="pic" library="images" name="logo.png"/>
</h:panelGroup>
<p:draggable for="pic" containment="parent"/>
The following screenshot shows the result achieved with the preceding code snippet.
Especially, we can see that the image has stayed in its boundaries although the cursor
has gone outside.
261
How it works
Horizontal or vertical dragging is possible by setting the axis attribute as axis="x" or
axis="y", which means that the draggable element can be dragged only horizontally or only
vertically, respectively.
Dragging on a grid is defined by the grid attribute. The value for dragging on a grid takes
comma-separated dimensions. For instance, grid="40,50" means that the draggable
element can be dragged in only 40 pixel steps horizontally and 50 vertically.
The containment attribute constraints dragging within the boundaries of the containment
element. Possible string values are parent, document, window, and [x1, y1, x2, y2].
The setting containment="parent" in the preceding example means that the draggable
element cannot go outside its parent.
See also
Refer to the Snapping to the edges of nearest elements recipe to learn about the more
advanced features of Draggable.
262
Chapter 8
How to do it
Generally, the snapping behavior is activated by setting the attribute snap to true. The
snapping behavior is configurable with two optionssnapMode and snapTolerance. The
first option, snapMode, determines which edges of snap elements the draggable component
will snap to. The second option, snapTolerance, determines a distance in pixels the
draggable component must be from the element when snapping is invoked.
<h:panelGroup id="snaptarget" layout="block"
styleClass="ui-widget-content"
style="height:150px;width:450px;">
<p class="ui-widget-header" style="margin:0;padding:5px;">
I'm a snap target to play with me
</p>
<p:draggable/>
</h:panelGroup>
<h:panelGroup id="defsnap" layout="block"
styleClass="dragSnap ui-widget-content">
<p>I'm with default snap and snap to all edges
of other draggable elements</p>
</h:panelGroup>
<p:draggable for="defsnap" snap="true"/>
<h:panelGroup id="outersnap" layout="block"
styleClass="dragSnap ui-widget-content">
<p>I only snap to the outer edges - try with the big box</p>
</h:panelGroup>
<p:draggable for="outersnap" snap="true" snapMode="outer"/>
<h:panelGroup id="innersnap" layout="block"
styleClass="dragSnap ui-widget-content">
<p>I only snap to the inner edges - try with the big box</p>
</h:panelGroup>
<p:draggable for="innersnap" snap="true"
snapMode="inner" snapTolerance="15"/>
263
How it works
The snapping is enabled by setting snap to true. If the snap attribute is set to false
(default), no snapping occurs. The first small h:panelGroup has no snapping options. It
snaps to the inner as well as outer boundaries of other draggable components. The second
h:panelGroup sets snapMode and can only snap to the outer boundaries. Possible values
of snapMode are inner, outer, and both. The third h:panelGroup also has a custom
snapTolerance parameter in addition to snapMode, set to inner. This is the distance in
pixels from the snap element's edges at which the snapping should occur. The default value is
20 pixels, but we have set it to 15.
In the current PrimeFaces implementation, a draggable component with
snap set to true snaps to all other draggable components. This is a little
bit different from jQuery's Draggable (http://jqueryui.com/
draggable), where we can also specify the elements that the draggable
component will snap to when it is close to the edge of such an element.
264
Chapter 8
How to do it
A component can be made droppable by using p:droppable. The component ID must match
the for attribute of p:droppable. If the for attribute is omitted, the parent component will
be selected as a droppable target. We will take two h:panelGroup components and make
them droppable and draggable, respectively. In addition, we will define a client-side callback
that gets invoked when a draggable component is dropped. This can be accomplished by the
onDrop attribute, which points to a JavaScript function.
<h:panelGroup id="drop" layout="block" styleClass="ui-widgetcontent"
style="height:150px; width:300px;">
<p class="ui-widget-header" style="margin:0; padding:5px;">
Drop here
</p>
<p:droppable onDrop="handleDrop"/>
</h:panelGroup>
<br/>
<h:panelGroup id="drag" layout="block"
styleClass="dragDiv ui-widget-content">
<p>Drag me to my target</p>
</h:panelGroup>
<p:draggable for="drag"/>
The client-side callback highlights the droppable h:panelGroup component and adds the
text Dropped! to the paragraph tag p, when invoked.
function handleDrop(event, ui) {
$(event.target).addClass("ui-state-highlight").
find("p").html("Dropped!");
}
265
How it works
The onDrop callback gets two parameters: event and ui, which are objects holding
information about the drag and drop event. The droppable target is accessible by event.
target. We use this fact to add the style class ui-state-highlight to the target. This
class is defined by jQuery ThemeRoller.
The event parameter is the original browser event, and ui is a prepared
object with the following properties:
See also
The most important style classes defined by jQuery ThemeRoller are described in the
Understanding structural and skinning CSS recipe in Chapter 2, Theming Concepts
266
Chapter 8
How to do it
Tolerance specifies which mode to use for testing if a draggable component is over
a droppable target. There are four different tolerance modes. They can be chosen by
the tolerance attribute of p:droppable. The following code snippet shows four
h:panelGroup components with settings for tolerance:
<h:panelGrid columns="4">
<h:panelGroup id="dropFit" layout="block"
styleClass="dropTarget ui-widget-content">
<p class="ui-widget-header">Drop here (tolerance = fit)</p>
<p:droppable onDrop="handleDrop" tolerance="fit"/>
</h:panelGroup>
<h:panelGroup id="dropIntersect" layout="block"
styleClass="dropTarget ui-widget-content">
<p class="ui-widget-header">Drop here (tolerance =
intersect)</p>
<p:droppable onDrop="handleDrop" tolerance="intersect"/>
</h:panelGroup>
<h:panelGroup id="dropPointer" layout="block"
styleClass="dropTarget ui-widget-content">
267
The scope attribute is used for acceptance. Its aim is to group sets of the draggable and
droppable components. Only a draggable component with the same scope value as a
droppable one will be accepted during drag and drop. The following code snippet shows two
draggable h:panelGroup components with different scope values. Only one can be dropped
onto the droppable h:panelGroup component with the ID dropTarget2.
<h:panelGroup id="dropTarget2" layout="block"
styleClass="ui-widget-content"
style="height:120px; width:300px;">
<p class="ui-widget-header" style="margin:0;padding:5px;">
Drop here
</p>
<p:droppable onDrop="handleDrop" scope="dnd"/>
</h:panelGroup>
<br/>
<h:panelGrid columns="2">
<h:panelGroup id="drag1" layout="block"
styleClass="dragDiv ui-widget-content">
<p>Drag me to my target</p>
<p:draggable scope="dnd"/>
268
Chapter 8
</h:panelGroup>
<h:panelGroup id="drag2" layout="block"
styleClass="dragDiv ui-widget-content">
<p>I'm draggable, but can't be dropped</p>
<p:draggable scope="dummy"/>
</h:panelGroup>
</h:panelGrid>
The following screenshot demonstrates that the handleDrop callback is not invoked when
the h:panelGroup with scope set to dummy gets dropped onto the h:panelGroup with
scope set to dnd:
How it works
The following table lists four tolerance modes that define the way to accept a draggable:
Mode
Description
fit
intersect
pointer
touch
269
There's more
In addition to scope, there is also the accept attribute. This is the jQuery selector that
defines the accepted components. Only the draggable components matching the selector will
be accepted by the droppable component.
How to do it
The following screenshots demonstrate the entire workflow:
270
Chapter 8
The first screenshot shows the dragging process from the list of available pizzas to the
order list.
271
The last screenshot demonstrates the removal process. One pizza has been dragged from the
order list and dropped into the trash list.
We will make the five pizza image tags h:graphicImage draggable.
<p:growl id="growl" escape="false"/>
<h:panelGrid id="selectPizza" columns="1">
<h:outputText value="Kiymali Pide" styleClass="text"/>
<h:graphicImage id="pizza1" styleClass="pizzaimage"
library="images" name="dragdrop/pizza1.png"
title="Kiymali Pide"/>
<h:outputText value="Kusbasi Pide" styleClass="text"/>
<h:graphicImage id="pizza2" styleClass="pizzaimage"
library="images" name="dragdrop/pizza2.png"
title="Kusbasi Pide"/>
<h:outputText value="Sucuklu Ve Yumurtali Pide"
styleClass="text"/>
<h:graphicImage id="pizza3" styleClass="pizzaimage"
library="images" name="dragdrop/pizza3.png"
title="Sucuklu Ve Yumurtali Pide"/>
<h:outputText value="Peynirli Pide" styleClass="text"/>
272
Chapter 8
<h:graphicImage id="pizza4" styleClass="pizzaimage"
library="images" name="dragdrop/pizza4.png"
title="Peynirli Pide"/>
<h:outputText value="Ispanakli Pide" styleClass="text"/>
<h:graphicImage id="pizza5" styleClass="pizzaimage"
library="images" name="dragdrop/pizza5.png"
title="Ispanakli Pide"/>
</h:panelGrid>
<p:draggable for="pizza1"
cursor="move"/>
<p:draggable for="pizza2"
cursor="move"/>
<p:draggable for="pizza3"
cursor="move"/>
<p:draggable for="pizza4"
cursor="move"/>
<p:draggable for="pizza5"
cursor="move"/>
helper="clone" revert="true"
helper="clone" revert="true"
helper="clone" revert="true"
helper="clone" revert="true"
helper="clone" revert="true"
Two h:panelGroup tags will be made droppable. One h:panelGroup tag is intended
to be used for the order list and one is for items removed from the order list. Droppable
p:droppable tags will get AJAX behaviors p:ajax attached with corresponding listeners in
each case. One listener should be invoked on pizza ordering and another on pizza removal.
<h:panelGroup id="order" layout="block" styleClass="ui-widgetcontent"
style="width:350px; padding:1px;">
<p class="ui-widget-header" style="margin:0;padding:5px;">
Order
</p>
<h:panelGroup layout="block" style="padding:10px;"
rendered="#{empty ajaxDragDrop.orderedPizza}">
Please drag and drop any available pizza to order it
</h:panelGroup>
<p:dataList id="orderedPizza"
value="#{ajaxDragDrop.orderedPizza}" var="op"
rendered="#{not empty ajaxDragDrop.orderedPizza}">
<h:panelGroup id="op" styleClass="text" layout="block">
<f:attribute name="pizza" value="#{op}"/>
273
Chapter 8
The corresponding CDI bean AjaxDragDrop adds an ordered pizza to the orderedPizza
list, and moves the pizza to the removedPizza list when it gets removed. This happens in the
listeners onPizzaOrder and onPizzaRemove, respectively.
@Named
@ViewScoped
public class AjaxDragDrop implements Serializable {
private List<String> orderedPizza = new ArrayList<String>();
private List<String> removedPizza = new ArrayList<String>();
public List<String> getOrderedPizza() {
return orderedPizza;
}
public List<String> getRemovedPizza() {
return removedPizza;
}
public void onPizzaOrder(DragDropEvent event) {
HtmlGraphicImage image = (HtmlGraphicImage) event.
getComponent().findComponent(event.getDragId());
String pizza = image != null ? image.getTitle() : "";
orderedPizza.add(pizza);
FacesMessage msg = new FacesMessage
(FacesMessage.SEVERITY_INFO,
"Selected pizza: " + pizza, null);
FacesContext.getCurrentInstance().addMessage(null, msg);
}
public void onPizzaRemove(DragDropEvent event) {
DataList dataList = (DataList) event.
getComponent().findComponent("orderedPizza");
FacesContext fc = FacesContext.getCurrentInstance();
dataList.invokeOnComponent(fc, event.getDragId(),
new ContextCallback() {
public void invokeContextCallback(FacesContext fc,
UIComponent comp) {
HtmlPanelGroup pGroup = (HtmlPanelGroup)comp;
275
How it works
To make h:graphicImage draggable, we use p:draggable with proper options:
helper="clone", revert="true", and cursor="move". The draggable images have
the title attributes set to the pizza names. This is important for getting the dropped
pizza's name in the onPizzaOrder listener by means of the findComponent() call.
The draggable h:panelGroup tag in the order list has, in contrast to h:graphicImage,
f:attribute with the pizza name as the value. This allows us to get the dropped pizza's
name from the component's attribute map in the onPizzaRemove listener by means of
the invokeOnComponent() call. Client IDs of draggable/droppable components can be
accessed by getDragId() or getDropId() on a DragDropEvent instance.
276
Chapter 8
Refer to the JSF 2 API documentation (http://javaserverfaces.
java.net/nonav/docs/2.2/javadocs/javax/faces/
component/UIComponent.html) to read more about
findComponent() and invokeOnComponent().
Last but not least, we use different ways to accept draggable. In the case of images, we set
accept to .pizzaimage. The accept attribute defines a jQuery selector for the accepted
draggable components. In the case of items in the order list, we set scope to trash. The
scope attribute is an alternative way to match the droppable and accepted draggable
components. What is easier to use in each particular case depends on the code.
There's more
We used two style classes with p:droppable:
They are used for better visual effects when dragging/dropping. If activeStyleClass is
specified, the class will be added to the droppable component while an acceptable draggable
component is being dragged. If hoverStyleClass is specified, the class will be added to the
droppable component while an acceptable draggable component is being dragged over it.
277
How to do it
For the purpose of better understanding the developed code, pictures come first. The first
screenshot shows what happens when we start to drag a document. The Recycle Bin area
gets highlighted as follows:
What it looks like after dropping three documents onto the Recycle Bin is reproduced in the
following screenshot:
278
Chapter 8
Available documents are represented as images within p:dataGrid. They are placed in
the panel components, which are made draggable. The dragging occurs via the panel's
titlebar. The titlebar contains the document's title (name). The recycle bin is represented by a
p:fieldset tag with the ID deletedDocs. Fieldset is made droppable. It also contains
a p:dataTable with the currently deleted document items. Whenever a document is being
dragged and dropped into the Recycle Bin, an AJAX listener is invoked. In the listener, the
dropped document is removed from the list of all available documents and added to the list of
deleted documents. Data iteration components will be updated after that in order to display
the correct data. The code snippet, in XHTML, looks as follows:
<p:fieldset legend="Available Documents">
<p:dataGrid id="availableDocs" columns="3" var="doc"
value="#{integrationDragDrop.availableDocs}">
<p:column>
<p:panel id="pnl" header="#{doc.title}"
style="text-align:center">
<h:graphicImage library="images"
name="dragdrop/#{doc.extension}.png"/>
</p:panel>
<p:draggable for="pnl" revert="true"
handle=".ui-panel-titlebar"
279
280
Chapter 8
The model class Document contains the document properties.
public class Document implements Serializable {
private
private
private
private
private
String title;
int size;
String creator;
Date creationDate;
String extension;
The bean IntegrationDragDrop creates available documents (they can be loaded from a
document management system, database, or filesystem), holds two lists for the data iteration
components, and provides the AJAX listener onDocumentDrop.
@Named
@ViewScoped
public class IntegrationDragDrop implements Serializable {
private List<Document> availableDocs =
new ArrayList<Document>();
private List<Document> deletedDocs =
new ArrayList<Document>();
@PostConstruct
public void initialize() {
availableDocs.add(new Document("Perl script", 120,
"Sara Schmidt", getCreationDate(), "perl"));
...
}
public List<Document> getAvailableDocs() {
return availableDocs;
281
How it works
We make the second p:fieldset tag droppable, and connect it to the p:dataList tag
with the ID availableDocs. This is done by setting datasource to availableDocs on
p:droppable. The AJAX listener onDocumentDrop, attached by the p:ajax tag, is invoked
on the drop event. Thanks to datasource, we can now access the dropped document
instance in the listener: Document doc = (Document)ddEvent.getData().
282
www.PacktPub.com
Stay Connected: