The droppable
component has a special integration with the data iteration components extending javax.faces.component.UIData
. Such PrimeFaces components are dataTable
, dataGrid
, dataList
, dataScroller
, carousel,
and ring
. The component tag p:droppable
defines a data source option as an ID of the data iteration component that needs to be connected with droppable
.
In this recipe, we will introduce a dataGrid
component containing some imaginary documents and make these documents draggable in order to drop them onto a recycle bin. The dataGrid
component will act as a data source for the droppable Recycle Bin.
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:
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" stack=".ui-panel" cursor="move"/> </p:column> </p:dataGrid> </p:fieldset> <p:fieldset id="deletedDocs" legend="Recycle Bin" style="margin-top:20px"> <p:outputPanel id="dropArea"> <h:outputText value="Drop documents into the recycle bin to delete them" rendered="#{empty integrationDragDrop.deletedDocs}" style="font-size:20px;"/> <p:dataTable var="doc" value="#{integrationDragDrop.deletedDocs}" rendered="#{not empty integrationDragDrop.deletedDocs}"> <p:column headerText="Title"> <h:outputText value="#{doc.title}"/> </p:column> <p:column headerText="Size (bytes)"> <h:outputText value="#{doc.size}"/> </p:column> <p:column headerText="Creator"> <h:outputText value="#{doc.creator}"/> </p:column> <p:column headerText="Creation Date"> <h:outputText value="#{doc.creationDate}"> <f:convertDateTime pattern="dd.MM.yyyy"/> </h:outputText> </p:column> </p:dataTable> </p:outputPanel> </p:fieldset> <p:droppable id="droppable" for="deletedDocs" tolerance="touch" activeStyleClass="ui-state-highlight" datasource="availableDocs"> <p:ajax listener="#{integrationDragDrop.onDocumentDrop}" update="dropArea availableDocs"/> </p:droppable>
The model class Document
contains the document properties.
public class Document implements Serializable { private String title; private int size; private String creator; private Date creationDate; private String extension; public Document(String title, int size, String creator, Date creationDate, String extension) { this.title = title; this.size = size; this.creator = creator; this.creationDate = creationDate; this.extension = extension; } // getters / setters ... }
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; } public List<Document> getDeletedDocs() { return deletedDocs; } public void onDocumentDrop(DragDropEvent ddEvent) { Document doc = (Document) ddEvent.getData(); deletedDocs.add(doc); availableDocs.remove(doc); } private Date getCreationDate() { Random random = new Random(); int day = random.nextInt(30); int month = random.nextInt(Calendar.DECEMBER + 1); int year = 2014; GregorianCalendar calendar = new GregorianCalendar(year, month, day); return calendar.getTime(); } }
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()
.
This recipe is available in the demo web application on GitHub (https://github.com/ova2/primefaces-cookbook/tree/second-edition). Clone the project if you have not done it yet, explore the project structure, and build and deploy the WAR file on every Servlet 3.x compatible application server, such as JBoss WildFly or Apache TomEE.
The showcase for the recipe is available at http://localhost:8080/pf-cookbook/views/chapter8/dragDropIntegration.jsf
.