Drag & Drop

compared with
Version 4 by Brad Kroeger
on Nov 17, 2010 13:09.


Key
These lines were removed. This word was removed.
These lines were added. This word was added.

View page history


There are 20 changes. View first change.

 h1. How to Use the ICEfaces Drag & Drop Functionality
  
 Drag and Drop can be used in ICEfaces to provide GUI features previously only available in traditional desktop applications. In this tutorial we will begin by making a simple Panel Group draggable and attaching a dragListener to communicate with a simple backing bean. We can then make use of Drag and Drop values to record specific data to the backing bean. Drag and Drop masks will reduce unnecessary communication over the ICEfaces AJAX bridge and improve efficiency.
  
 !dndBasics.png!
  
 Drag and Drop functionality can be added to any Panel Group. Keep in mind, you can nest any number of ICEfaces and/or HTML tags in a Panel Group. Typically simple images or text are used.
  
 *Note about Z-Index:* for typical purposes, Draggable Panel Groups should not be obscured by other elements on a page. To avoid this, you can set the Z-Index in the component's CSS style to an arbitrarily high number.
  
 ----
 \\
 {panel}The rest of this tutorial will discuss the following topics:
  
 * [Creating a Draggable Panel|#creating]
 * [Drag Values and Drop Values|#values]
 * [Using Drag and Drop Masks|#masks]
 * [Drag and Drop Options|#options]
 * [Tutorial Source Code Downloads|#downloads]
 {panel}
 \\
 ----
  
 h3. {anchor:creating}Creating a Draggable Panel
 \\
 To make a Panel Group draggable, set its _draggable_ attribute to "true".
  
 {code:xml}
 <ice:panelGroup draggable="true">
  ...
 </ice:panelGroup>
 {code}
  
 We can add some style information and fix the size of the panel to avoid scrollbars that are introduced when moving a Panel Group that is 100% of the page width. Setting the Z-Index ensures that the Draggable Panel is not obscured by other block elements. A background colour makes the outline of the Draggable Panel easily identifiable and distinct from other elements. Finally, the addition of cursor:move to the style will change the mouse cursor to indicate that the Panel can be dragged.
  
 {code:xml}
 <ice:panelGroup style="z-index:10; width:100px; height: 100px; background-color:silver; cursor:move;"
  draggable="true"
  dragListener="#{dragDropBean.dropPanelListener}">
  <h3>Drag</h3>
 </ice:panelGroup>
 {code}
  
 A dragListener will receive each and every DragEvent by default (see Using Drag and Drop Masks). We can record each event's type and append it to a String, using an outputText to display it.
  
 {code:xml}
 <h2>Drag message:</h2>
 <ice:outputText value="#{dragDropBean.dragMessage}" />
 {code}
  
 The following code is taken from DragDropBean.java which uses a simple dragListener method to record the event types to a String.
  
 {code:title=DragDropBean.java|borderStyle=solid}
 import com.icesoft.faces.component.dragdrop.DragEvent;
  
 ...
  
 private String dragMessage = "";
  
 public void dragListener(DragEvent dragEvent){
  dragMessage += ", " + DragEvent.getEventName(dragEvent.getEventType());
 }
  
 ...
  
 public String getDragMessage () {
  return dragMessage;
  
 {code}
  
 In addition to a Draggable Panel, we can add Drop Target Panels. When a Draggable Panel is dropped on a Drop Target Panel, the "dropped" DragEvent will occur. These Drop Targets will also allow the remaining DragEvents to occur.
  
 {code:xml}
 <!-- Drop Target 1 -->
 <ice:panelGroup style="margin:10px; padding:10px; width:300px; height:200px; background-color:orange; color:white;"
  dropTarget="true">
  <em>Drop Target 1</em>
 </ice:panelGroup>
  
 <!-- Drop Target 2 -->
 <ice:panelGroup style="margin:10px; padding:10px; width:300px; height:200px; background-color:blue; color:white;"
  dropTarget="true">
  <em>Drop Target 2</em>
 </ice:panelGroup>
 {code}
 \\
 \\
 ----
  
 h3. {anchor:values}Adding a Data Paginator
  h3. {anchor:values}Drag Values and Drop Values
 \\
The dataTable component is good at displaying large set of data but there are many user interface (UI) scenarios where only a limited subset of the data can or should be shown. The dataPaginator component works in conjunction with the dataTable component allowing manipulation of the view of the dataTable data model. The following screen shot shows two dataPaginator components; the first provides paging navigation control and the other shows the current state of the dataTable model.
  Draggable panels can be assigned values. These values will be contained in the DragEvent received by a dragListener in the Backing Bean. Although they support value binding expressions for their values, a simple String will provide an adequate demonstration of their use.
  
!dataTable-paginator_screenshot.png!
  We can assign each of the Drop Targets the values "Target 1" and "Target 2" for their Drop values. An outputText element on the Draggable Panel itself will display the stored String.
  
The first step in adding a dataPaginator control component to a dataTable is to ensure that the dataTable has a unique _id_ attribute assigned to it and that the number of rows in a page view is set via the _rows_ attribute. The dataPaginator components' _for_ attribute is then set to the dataTable _id_ value, this binds the dataPaginator to the dataTable's model. The next step in adding a dataPaginator is to set the attribute _paginator_ to true, this insures that the control links around defined facet controls will be rendered. The following JSF code snippet is of a dataPaginator control where the fastforward and fastrewind facets will move ahead three pages and the maximum number of pages controls shown at the same time is set to four:
  
 {code:xml}
<ice:dataPaginator id="dataScroll_3"
  for="inventoryList"
 paginator="true"
 fastStep="3"
  paginatorMaxPages="4">
  ...
 <ice:dataTable rows="5"
  id="inventoryList"
  value="#{inventoryList.carInventory}"
  var="item">
 {code}
  <!-- Drop Target 1 -->
 <ice:panelGroup dropValue="Target 1"
  style="margin:10px; padding:10px; width:300px; height:200px; background-color:orange; color:white;" dropTarget="true">
  <em>Drop Target 1</em>
 </ice:panelGroup>
  
The next step in adding a dataPaginator is to define the facets which will add control links to their child components. There are are six facets which can be optionally implemented; _first_, _last_, _previous_, _next_, _fastforward_ and _fastrewind_. The following is the JSF code needed add a _first_ facet which will allow users to click on an image graphic to move to the first page of the dataTable model:
  <!-- Drop Target 2 -->
 <ice:panelGroup dropValue="Target 2"
  style="margin:10px; padding:10px; width:300px; height:200px; background-color:blue; color:white;" dropTarget="true">
  <em>Drop Target 2</em>
 </ice:panelGroup>
  
{code:xml}
 <f:facet name="first">
  <ice:graphicImage url="./xmlhttp/css/xp/css-images/arrow-first.gif"
  style="border:none;"
  title="First Page"/>
 </f:facet>
 {code}
  
The dataPaginator can also be used for displaying information about the dataTable's model by setting the _paginator_ attribute to false (default value). There are several attributes on the dataPaginator component which allow a developer to bind user defined variable names to the dataTable model:
  
 * _rowsCountVar_ -- number of rows in the dataTable view model.
 * _displayedRowsCountVar_ -- number of rows shown in this dataTable view model.
 * _firstRowIndexVar_ -- index of first row displayed in this dataTable view model.
 * _lastRowIndexVar_ -- index of the last row displayed in this dataTable view model.
 * _pageCountVar_ -- number of pages that make up dataTable view model.
 * _pageIndexVar_ -- current page number in the dataTable view model.
  
 The following code shows how bound user defined variables can be used to display the dataTable's model state:
  
 {code:xml}
 <ice:dataPaginator id="dataScroll_2" for="inventoryList"
  rowsCountVar="rowsCount"
  displayedRowsCountVar="displayedRowsCount"
  firstRowIndexVar="firstRowIndex"
  lastRowIndexVar="lastRowIndex"
  pageCountVar="pageCount"
  pageIndexVar="pageIndex">
  <ice:outputFormat styleClass="standard"
  value="{0} cars found, displaying {1} car(s), from {2} to {3}. Page {4} / {5}.">
  <f:param value="#{rowsCount}"/>
  <f:param value="#{displayedRowsCount}"/>
  <f:param value="#{firstRowIndex}"/>
  <f:param value="#{lastRowIndex}"/>
  <f:param value="#{pageIndex}"/>
  <f:param value="#{pageCount}"/>
  </ice:outputFormat>
 </ice:dataPaginator>
 {code}
  !dndValues.png!
 \\
 \\
 ----
  
h3. {anchor:sortable}Adding a Sortable Header
  h3. {anchor:masks}Drag and Drop Masks
 \\
This tutorial has already shown how easy it is to use the dataTable with the dataPaginator component. It is also possible to add sorting support to the dataTable via commandSortHeader. The commandSortHeader component adds a commandLink to the dataTable header which when clicked, will toggle the ordering of the parent column data. The commandSortHeader can be added to all or just of few dataTable headers. The following screen shot shows the commandSortHeader being used in the dataTable-sortable example to sort the inventory by stock number:
  Drag and Drop masks can be used to prevent certain Drag and Drop events from initiating a server round-trip. By masking unnecessary events, the application will make more efficient use of resources and increase performance.
  
!dataTable-sortable_screenshot.png!
  The attributes dropMask and dragMask of a Panel Group can be assigned a comma-separated list of values corresponding to the different event masks.
  
In this section we will add a commandSortHeader component to each inventory column shown in the previous example. The first step in this process is to modify the TableBean backing bean by extending SortableList.java. The SortableList class is provided for convenience but its functionality could just as easily be added to the base class.
  * *hover_start*: started to hover over a drop target
 * *hover_end*: stopped hovering over a drop target
 * *dragged*: is being dragged (not necessarily moving)
 * *dropped*: has been dropped on a drop target
 * *drag_cancel*: has been dropped but not on a drop target
  
The first step in adding a commandSortHeader is to add the attributes _sortColumn_ and _sortAscending_ to the dataTable component. These two attributes provide bindings which are manipulated when a commandSortHeader is activated. The _sortColumn_ attribute is updated to the column name of the selected commandSortHeader and the _sortAscending_ attribute's boolean value is toggled if the same commandSortHeader is activated. The following JSF code snippet shows the modified dataTable decoration:
  Typically, we will only want to listen for only one or two of these events. In this tutorial we're only interested in "dropped" events.
  
 {code:xml}
<ice:dataTable id="dataSortData"
  sortColumn="#{inventoryList.sortColumnName}"
  sortAscending="#{inventoryList.ascending}"
  value="#{inventoryList.carInventory}"
  var="item">
  <!-- Draggable Panel -->
 <ice:panelGroup style="z-index:10; width:100px; height: 100px; background-color:silver; cursor:move;"
  draggable="true"
  dragMask="hover_start, dragging, hover_end, drag_cancel"
  dragListener="#{dragDrop.dragListener}">
  <h3>Drag Me</h3>
  <ice:outputText value="#{dragDrop.dropValue}" />
  </ice:panelGroup>
 {code}
  
The next step in updating the dataTable for the commandSortHeader component is to add the component to a column header facet. The following code shows the addition of a commandSortHeader component to the first column.
  
 {code:xml}
 <!-- Stock number -->
 <ice:column>
  <f:facet name="header">
  <ice:commandSortHeader columnName="#{inventoryList.stockColumnName}"
  arrow="true" >
  <ice:outputText value="#{inventoryList.stockColumnName}"/>
  </ice:commandSortHeader>
  </f:facet>
  <ice:outputText value="#{item.stock}"/>
 </ice:column>
 {code}
  
 The commandSortHeader in this example wraps the output text for the header and adds an arrow which will indicate the ordering applied to the table data. To help avoid typing errors the 'columnName' attribute is bound to the backing bean, the column name is used to specify which column should be sorted.
  
 Now that all the JSF code is in place there is still a little work to be done to the backing bean to get the data sorted. As mentioned earlier, the commandSortHeader component only manipulates the dataTable component's _sortColumn_ and _sortAscending_ attributes and as a result it is up to the table's backing bean to use this information to provide sorted data. The following sort method shows how we sort the dataTable data. Remember that _sortColumn_ is bound to the sortColumnName property and that _sortAscending_ is bound to the ascending property in the TableBean class.
  
 {code:title=TableBean.java|borderStyle=solid}
 protected void sort() {
  Comparator comparator = new Comparator() {
  public int compare(Object o1, Object o2) {
  InventoryItem c1 = (InventoryItem) o1;
  InventoryItem c2 = (InventoryItem) o2;
  if (sortColumnName == null) {
  return 0;
  }
  if (sortColumnName.equals(stockColumnName)) {
  return ascending ?
  Integer.valueOf(c1.getStock()).compareTo(Integer.valueOf(c2.getStock())) :
  Integer.valueOf(c2.getStock()).compareTo(Integer.valueOf(c1.getStock()));
  } else if (sortColumnName.equals(modelColumnName)) {
  return ascending ? c1.getModel().compareTo(c2.getModel()) :
  c2.getModel().compareTo(c1.getModel());
  } else if (sortColumnName.equals(descriptionColumnName)) {
  return ascending ? c1.getDescription().compareTo(c2.getDescription()) :
  c2.getDescription().compareTo(c1.getDescription());
  } else if (sortColumnName.equals(odometerColumnName)) {
  return ascending ?
  Integer.valueOf(c1.getOdometer()).compareTo(Integer.valueOf(c2.getOdometer())) :
  Integer.valueOf(c2.getOdometer()).compareTo(Integer.valueOf(c1.getOdometer()));
  } else if (sortColumnName.equals(priceColumnName)) {
  return ascending ?
  Integer.valueOf(c1.getPrice()).compareTo(Integer.valueOf(c2.getPrice())) :
  Integer.valueOf(c2.getPrice()).compareTo(Integer.valueOf(c1.getPrice()));
  } else return 0;
  }
  };
  Arrays.sort(carInventory, comparator);
 }
  
 {code}
  
 When a commandSortHeader is selected the JSF lifecycle is called and the dataTable values will be retrieved from the backing bean. It is during this dataTable retrieval that the data should be sorted but only if the _sortColumnName_ and _sortAscending_ values have changd. The following code snippet shows the getCarInventory() method has been modified to only sort the array data when required:
  
 {code:title=TableBean.java|borderStyle=solid}
 public InventoryItem[] getCarInventory() {
  // we only want to sortColumnName if the column or ordering has changed.
  if (!oldSort.equals(sortColumnName) ||
  oldAscending != ascending){
  sort();
  oldSort = sortColumnName;
  oldAscending = ascending;
  }
  return carInventory;
 }
 {code}
  
 In this tutorial we have shown how to add sortable data columns to a dataTable and when combined with the comandSortHeader the dataTable becomes a very flexible and powerful component. The next and last section of this tutorial will take a look at how to customize the dataTable with CSS.
  !dndMasks.png!
 \\
 \\
 ----
  
h3. {anchor:style}Customising the Data Table CSS style
  h3. {anchor:options}Drag and Drop Options
 \\
The ice:dataTable, like most JSF components can be styled using CSS. If no style classes are specified, the component will use a set of default names that are defined in the XP, Royale and Rime CSS sheets. The default style sheet implementation provides header background images and basic border colours. In this part of the tutorial will will focus on two style attributes, _columnClasses_ and _rowClasses_.
  Similar to the Drag and Drop masks, Drag and Drop options are defined in a comma-separated list of values for the attribute dragOptions. These options affect the visual nature of the dragging itself:
  
The _columnClasses_ attribute can take one or more CSS class names separated by commas. These classes are then iterated over the total number of columns in the table. For example, if there are six columns in a table and two CSS style classes defined in the _columnClasses_ attribute, then the first column will be given the first defined column style, the second column will be given the second defined column, the third column will be given the first defined style and so on. The iterative style assignment can be quite useful for applying column specific styles to either highlight even and odd columns are to give a particular column specialized styling such as text-alignment. The following code snippet shows how we will define the _columnClasses_ attribute for this example.
  * *revert* - when a draggable is dropped, the draggable will move back to it's starting position.
 * *ghosting* - when a draggable is dragged, a ghost copy is left in the original position.
 * *solid* - do not make the panel transparent when dragging.
  
{code:xml}
 columnClasses="stockColumn, modelColumn, desriptionColumn, odometerColumn, priceColumn"
 {code}
  
 We will be specifying a custom style for each of the five columns. The following code is the CSS that defines the five column styles.
  
 {code:title=style.css|borderStyle=solid}
 /* common to all columns*/
 .stockColumn, .modelColumn, .desriptionColumn, .odometerColumn,
 .priceColumn{
  border-right: 1px solid #666666;
  border-bottom: 1px solid #666666;
 }
  
 .stockColumn{
  width: 60px;
 }
  
 .modelColumn{
  width: 225px;
 }
  
 .desriptionColumn{
  width: 150px;
 }
  
 .odometerColumn{
  width: 75px;
 }
  
 .priceColumn{
  width: 75px;
 }
 {code}
  
 The _rowClasses_ attribute is used the same way as the _columnClasses_ attribute by the dataTable renderer. Instead of specifying a style class for each row we will specify two style classes which will be applied to every odd then even row of the dataTable. The following screen shot shows what the dataTable looks like with both _columnClasses_ and _rowClasses_ decorations.
  
 !dataTable-style_screenshot.png!
  
 The dataTable renders an HTML table as the main construct for the rendered dataTable component. The _styleClass_ attribute value will be rendered as a class attribute for the rendered table. If you wish to change the default header styles you must override ICEfaces' predefined CSS names. These names are as follows:
  
 ||CSS Class Name||Description||
 |iceTblHeader|Default CSS class name applied to all table header cells.|
 |iceTblHeader a|Default CSS style applied anchor tags in a table header cell.|
  !dndOptions.png!
 \\
 \\
 ----
  
 h3. {anchor:downloads}Tutorial Source Code Downloads
 \\
 || Example || Source || Notes ||
 | dataTable-basic |[dataTable-basic source code |^dataTable-basic-tutorial.zip|Download Source Code]| Simple example of how to dispaly data uses a dataTable component. |
 | dataTable-paginator |[dataTable-paginator source code |^dataTable-paginator-tutorial.zip|Download Source Code]| A dataPaginator is added to the dataTable used in dataTable-basic |
 | dataTable-sortable |[dataTable-sortable source code |^dataTable-sortable-tutorial.zip|Download Source Code]| CommandSortHeaders are added to each column in a data table. |
 | dataTable-style |[dataTable-style source code |^dataTable-style-tutorial.zip|Download Source Code]| The row and column classes are altered with CSS. |
  | dragDrop-basic |[dragDrop-basic source code |^dragDrop-basic-tutorial.zip|Download Source Code]| Basic example of how to setup a Draggable Panel and its backing bean. |
 | dragDrop-values |[dragDrop-values source code |^dragDrop-values-tutorial.zip|Download Source Code]| Drag and Drop values are used to assign a String in a backing bean. |
 | dragDrop-masks |[dragDrop-masks source code |^dragDrop-masks-tutorial.zip|Download Source Code]| Drag and Drop masks are used to prevent unneccessary DragEvent's from firing. |
 | dragDrop-options |[dragDrop-options source code |^dragDrop-options-tutorial.zip|Download Source Code]| Different Drag and Drop options are shown (ghosting, revert, etc.). |

© Copyright 2017 ICEsoft Technologies Canada Corp.