Skip Headers
Oracle® Fusion Middleware Web User Interface Developer's Guide for Oracle Application Development Framework
11g Release 1 (11.1.1)

Part Number B31973-02
Go to Documentation Home
Home
Go to Book List
Book List
Go to Table of Contents
Contents
Go to Feedback page
Contact Us

Go to previous page
Previous
Go to next page
Next
View PDF

29 Adding Drag and Drop Functionality

This chapter describes how to add functionality to your pages that allows users to drag the value of one component's attribute and drop it on to another, drag items from one collection to another, or drag to change the parent of a component, or the order of the components when the parent holds multiple components.

This chapter includes the following sections:

29.1 Introduction to Drag and Drop Functionality

The ADF Faces framework allows you to configure a component so that an attribute value can be dragged to another component causing that value to become the new value for the attribute. For example, as shown in Figure 29-1, you can configure a panelBox component so that its text attribute can get its value by dragging an outputText component on to the title. In this case, the panelBox component's text attribute gets its value from the value attribute of the dragged outputText component.

Figure 29-1 Dragging and Dropping an Attribute

You can drag and drop an attribute value

The component that will accept the drop is called the target. The component that will be dragged and contains the value is called the source. To configure a component to be a target, you add the attributeDropTarget tag as a child to the component. You configure this tag with the attribute it should populate. To configure the source, you add the attributeDragSource tag, also configured with the attribute providing the value. In Figure 29-1, the panelBox component has a child attributeDropTarget tag whose attribute value is text, the attribute to be populated. Each of the outputText components has an attributeDragSource tag whose attribute value is value, which is the attribute value to be dragged.

Using the attributeDropTarget tag allows you to only copy an attribute value from one component to another. There may be cases when you want to move the value instead of copy it. Or you may want more flexibility that allows you to drag and drop an actual object. In these cases, instead of using the attributeDropTarget tag, you use the dropTarget tag. This tag allows you to configure valid actions for the value, either copy (copy and paste), move (cut and paste), or link (copy and paste as a link; for example copying text and pasting the text as an actual URL).

It also provides more flexibility in that you implement a listener for the DropEvent event where you can add any required logic. Additionally, you can implement a JavaScript client listener that will invoke logic on the client in response to the drop event.

When you use a dropTarget tag, you can restrict the Java type of the object that can be dropped by adding a dataFlavor tag. This helps when the target can accept only one object type but the source value may be one of a number of types. The dataFlavor tag also allows you to set multiple types so that the target can accept objects from more than one source or from a source that may contain more than one type. Both the target and the source must contain the dataFlavor tag, and the values must be the same in order for the drop to be successful.

Along with dragging attribute values or objects, you can also drag collections. The collectionDropTarget and collectionDragSource tags allow you to drag and drop objects from one collection into another. For example, in the File Explorer application, users can drag a file from the table that displays directory contents to any folder in the directory tree. Figure 29-2 shows the File0.doc object being dragged from the table displaying the contents of the Folder0 directory to the Folder3 directory. Once the drop is complete, the object will become part of the collection that makes up Folder3.

Figure 29-2 Drag and Drop Functionality in the File Explorer Application

Drag a file to a directory

As with dragging and dropping single objects, you can have a drop cause a copy, move, or copy and paste as a link (or a combination of the three), and use dataFlavor tags to limit what a target will accept. As with the dropTarget tag, you must implement listeners to handle any logic both before and after the drop.

In more complex pages, you may have multiple targets, and it may happen that they all accept the same type. In order to further restrict what can be dropped on a target, you can set the discriminator attribute on the dataFlavor tag with a unique String value. Only sources that have the same discriminant value can be dropped.

Note:

Discriminant values work only when dragging and dropping collections.

You can also drag and drop components. For example, you can drag panelBox components on the page to change their order.

Note:

Drag and drop functionality is not supported between windows. Any drag that extends past the window boundaries will be canceled. Drag and drop functionality is supported between pop-up windows and the base page for the pop-up.

Also note that drag and drop functionality is not accessible; that is, there are no keyboard strokes that can be used to execute a drag and drop. Therefore, if your application requires all functionality to be accessible, you must provide this logic.

29.2 Adding Drag and Drop Functionality for Attributes

You add drag and drop functionality for attributes by defining one component's attribute to be a target and another component's attribute to be a source.

Note:

The target and source attribute values must both be the same data type.

The following procedure assumes you have your target and source components already on the JSF page.

To add drag and drop functionality for attributes:

  1. In the Component Palette, expand the Operations section and drag and drop an Attribute Drop Target as a child to the target component.

  2. In the Insert Attribute Drop Target dialog, use the Attribute dropdown list to select the attribute that will be populated by the drag and drop action. This dropdown list shows all valid attributes on the target component.

  3. From the Component Palette, drag and drop an Attribute Drag Source as a child to the component that can provide a value for the target.

  4. In the Insert Attribute Drag Source dialog, use the Attribute dropdown list to select the attribute whose value will be used to populate the target attribute. This dropdown list shows all valid attributes on the source component.

29.3 Adding Drag and Drop Functionality for Objects

When you want users to be able to drag things other than attribute values, or you want users to be able to do something other than copy attributes from one component to another, you use the dropTarget tag. Additionally, use the DataFlavor object to determine the valid Java types of sources for the drop target. Because there may be several drop targets and drag sources, you can further restrict valid combinations by using discriminant values. You also must implement any required functionality in response to the drag and drop action.

For example, suppose you have an outputText component and you want the user to be able to drag the outputText component to a panelBox component and have that component display an array, as shown in Figure 29-3.

Figure 29-3 Dragging and Dropping an Array Object

Drag and drop an object

The outputText component contains an attributeDragSource tag. However, because you want to drag an array (and not just the String value of the attribute), you must use the dropTarget tag instead of the attributeDropTarget tag. Also use a dataFlavor tag to ensure that only an array object will be accepted on the target.

You can also define a discriminant value for the dataFlavor tag. This is helpful if you have two targets and two sources, all with the same object type. By creating a discriminant value, you can be sure that each target will accept only valid sources. For example, suppose you have two targets that both accept an EMPLOYEE object, TargetA and TargetB. Suppose you also have two sources, both of which are EMPLOYEE objects. By setting a discriminant value on TargetA with a value of alpha, only the EMPLOYEE source that provides the discriminant value of alpha will be accepted.

You also must implement a listener for the drop event. The object of the drop event is called the transferable, which contains the payload of the drop. Your listener must access the transferable object, and from there, use the DataFlavor object to verify that the object can be dropped. You then use the drop event to get the target component and update the property with the dropped object. More details about this listener are covered in the procedure in Section 29.3.1, "How to Add Drag and Drop Functionality for a Single Object".

29.3.1 How to Add Drag and Drop Functionality for a Single Object

To add drag and drop functionality, first add tags to a component that define it as a target for a drag and drop action. Then implement the event handler method that will handle the logic for the drag and drop action. Last, you define the sources for the drag and drop.

This procedure assumes the source and target components already exist on the page.

To add drag and drop functionality:

  1. In the JSF page that contains the target, add a dropTarget tag as a child to the target component by dragging and dropping a Drop Target tag (located in the Operations panel) from the Component Palette.

  2. In the Insert Drop Target dialog, enter an expression that evaluates to a method on a managed bean that will handle the event (you will create this code in Step 6).

    Tip:

    You can also intercept the drop on the client by populating the clientDropListener attribute. For more information, see Section 29.3.3, "What You May Need to Know About Using the ClientDropListener".
  3. With the dropTarget tag still selected, in the Property inspector, select a value for the actions attribute. This defines what actions are supported by the drop target. Valid values can be COPY (copy and paste), MOVE (cut and paste), and LINK (copy and paste as a link), for example:.

    MOVE COPY
    

    If no actions are specified, the default is COPY.

  4. Create a dataFlavor tag by dragging a Data Flavor tag (located in the Operations panel), from the Component Palette and dropping it as a child to the dropTarget tag. This tag determines the type of object that can be dropped onto the target, for example a String or a Date. Multiple dataFlavor tags are allowed under a single drop target to allow the drop target to accept any of those types.

  5. In the Insert Data Flavor dialog, enter the class for the object that can be dropped onto the target, for example java.lang.Object.

    Tip:

    To specify a typed array in a DataFlavor tag, add brackets ([]) to the class name, for example, java.lang.Object[].

    Example 29-1 shows the code for a dropTarget component inserted into an panelBox component that takes an array object as a drop source. Note that because an action was not defined, the only allowed action will be COPY.

    Example 29-1 JSP Code for a dropTarget tag

    <af:panelBox text="PanelBox2">
      <f:facet name="toolbar"/>
      <af:dropTarget dropListener="#{myBean.handleDrop}">
        <af:dataFlavor flavorClass="java.lang.object[]"/>
      </af:dropTarget>
    </af:panelBox>
    
  6. In the managed bean referenced in the EL expression created in Step 2, create the event handler method (using the same name as in the EL expression) that will handle the drag and drop functionality.

    This method must take a DropEvent event as a parameter and returns a DnDAction object, which is the action that will be performed when the source is dropped. Valid return values are DnDAction.COPY, DnDAction.MOVE, and DnDAction.LINK, and were set when you defined the target attribute in Step 3. This method should check the DropEvent event to determine whether or not it will accept the drop. If the method accepts the drop, it should perform the drop and return the DnDAction object it performed. Otherwise, it should return DnDAction.NONE to indicate that the drop was rejected.

    The method must also check for the presence for each dataFlavor object in preference order.

    Tip:

    If your target has more than one defined dataFlavor object, then you can use the Transferable.getSuitableTransferData() method, which returns a List of TransferData objects available in the Transferable object in order, from highest suitability to lowest.

    The DataFlavor object defines the type of data being dropped, for example java.lang.Object, and must be as defined in the DataFlavor tag on the JSP, as created in Step 5.

    Tip:

    To specify a typed array in a DataFlavor object, add brackets ([]) to the class name, for example, java.lang.Object[].

    DataFlavor objects support polymorphism so that if the drop target accepts java.util.List, and the transferable object contains a java.util.ArrayList, the drop will succeed. Likewise, this functionality supports automatic conversion between Arrays and Lists.

    If the drag and drop framework doesn't know how to represent a server DataFlavor object on the client component, the drop target will be configured to allow all drops to succeed on the client.

    Example 29-2 shows a private method that the event handler method calls (the event handler itself does nothing but call this method; it is needed because this method also needs a String parameter that will become the value of the outputText component in the panelBox component). This method copies an array object from the event payload and assigns it to the component that initiated the event.

    Example 29-2 Event Handler Code for a dropListener

    public DnDAction handleDrop(DropEvent dropEvent) 
    {
      Transferable dropTransferable = dropEvent.getTransferable();
              Object[] drinks = dropTransferable.getData(DataFlavor.OBJECT_ARRAY_FLAVOR);
            
        if (drinks != null)
        {
          UIComponent dropComponent = dropEvent.getDropComponent();
              
    // Update the specified property of the drop component with the Object[] dropped
          dropComponent.getAttributes().put(propertyName, Arrays.toString(drinks));
              
          return DnDAction.COPY;
        }
        else
        {
          return DnDAction.NONE;      
        }    
      }   
    
  7. Add a clientAttribute tag as a child to the source component by dragging a Client Attribute (located in the Operations panel), from the Component Palette. This tag is used to define the payload of the source for the event. Define the following for the clientAttribute tag in the Property Inspector:

    • Name: Enter any name for the payload.

    • Value: Enter an EL expression that evaluates to the value of the payload. In the drinks example, this would resolve to the Array that holds the different drink values.

  8. Drag and drop an Attribute Drag Source (located in the Operations panel), from the palette as another child to the source component. In the Insert Attribute Drag Source dialog, use the dropdown list to select the name defined for the clientAttribute tag created in the previous step. Doing so makes the value of the clientAttribute tag the source's payload. Example 29-3 shows the code for an outputText component that is the source of the drag and drop operation.

    Example 29-3 JSP Code for a Drag Source

    <af:outputText value="Drag to see drinks">
      <af:clientAttribute name="drinks" value="#{myBean.drinks}"/>
      <af:attributeDragSource attribute="drinks"/>
    </af:outputText>
    

29.3.2 What Happens at Runtime

When performing a drag and drop operation, users can press keys on the keyboard (called keyboard modifiers) to select the action they wish to take on a drag and drop. The drag and drop framework supports the following keyboard modifiers:

  • SHIFT: MOVE

  • CTRL: COPY

  • ATL: LINK

When a user executes the drag and drop operation, the drop target first determines that it can accept the drag source's data flavor value. Next, if the source and target are collections, the framework intersects the actions allowed between the drag source and drop target and executes the action (one of COPY, MOVE, or LINK) in that order from the intersection. When there is only one valid action, that action is executed. When there is more than one possible action and the user's keyboard modifier matches that choice, then that is the one that is executed. If either no keyboard modifier is used, or the keyboard modifier used does not match an allowed action, then the framework chooses COPY, MOVE, LINK in that order, from the set of allowed actions.

For example, suppose you have a drop target that supports COPY and MOVE. First the drop target determines that drag source is a valid data flavor. Next, it determines which action to perform when the user performs the drop. In this example, the set is COPY and MOVE. If the user holds down the SHIFT key while dragging (the keyboard modifier for MOVE), the framework would choose the MOVE action. If the user is doing anything other than holding down the SHIFT key when dragging, the action will be COPY because COPY is the default when no modifier key is chosen (it is first in the order). If the user is pressing the CTRL key, that modifier matches COPY, so COPY would be performed. If the user was pressing the ALT key, the action would still be COPY because that modifier matches the LINK action which is not in the intersected set of allowed actions.

Note:

Because information is lost during the roundtrip between Java and JavaScript, the data in the drop may not be the type that you expect. For example, all numeric types appear as double objects, char objects appear as String objects, List and Array objects appear as List objects, and most other objects appear as Map objects. For more information, see Section 5.4.3, "What You May Need to Know About Marshalling and Unmarshalling of Data".

29.3.3 What You May Need to Know About Using the ClientDropListener

The dropTarget tag contains the clientDropListner attribute where you can reference JavaScript that will handle the drop event on the client. The client handler should not take any parameters and returns an AdfDnDContext action. For example, if the method returns AdfDnDContext.ACTION_NONE the drop operation will be canceled and no server call will be made; if the method returns AdfDnDContext.ACTION_COPY, a copy operation will be allowed and a server call will be made which will execute the dropListener method if it exists.

For example, suppose you want to log a message when the drop event is invoked. You might create a client handler to handle logging that message and then returning the correct action so that the server listener is invoked. Example 29-4 shows a client handler that uses the logger to print a message.

Example 29-4 clientDropListener Handler

<script>
/**
 * Shows a message.
 */
function showMessage()
{
  AdfLogger.LOGGER.logMessage(AdfLogger.ALL, "clientDropListener handler,
    copying...");
  return AdfDnDContext.ACTION_COPY;
}
</script>

29.4 Adding Drag and Drop Functionality for Collections

You can add drag and drop functionality that allows users to drag an item from a collection (for example, a row from a table), and drop it into another collection component such, as a tree. For example, suppose you have one table and you want to be able to drag a row from it and drop it to another table, as shown in Figure 29-4.

Figure 29-4 Drag and Drop Functionality Between Collections

Drag a file and drop to a directory

In this example, when the drag and drop action is completed, the dropListener attribute on the drop target table accesses a handler that you implement which gets the row's data using the rowKey object and then copies it to a new row in the table.

When the target source is a collection and it supports the move operation, you may also want to also implement a method for the dragDropEndListener attribute, which is referenced from the source component and is used to clean up the collection after the drag and drop operation. For more information, see Section 29.4.2, "What You May Need to Know About the dragDropEndListener".

29.4.1 How to Add Drag and Drop Functionality for Collections

To add drag and drop functionality for collections, instead of using the dragTarget tag, you use the collectionDropTarget tag. You then must implement the event handler method that will handle the logic for the drag and drop action. Next, you define the source for the drag and drop operation. If the source is also a collection, use the collectionDragSource tag instead of the attributeDragSource tag.

This procedure assumes you already have the source and target components on the page.

To add drag and drop functionality:

  1. Add a collectionDropTarget tag as a child to the target collection component by dragging a Collection Drop Target from the Component Palette.

  2. In the Insert Collection Drop Target dialog, enter an expression for the dropListener attribute that evaluates to a method on a managed bean that will handle the event (you will create this code in Step 4).

  3. In the Property Inspector, set the following:

    • actions: Select the actions that can be performed on the source during the drag and drop operation.

      If no actions are specified, the default is COPY.

    • modelName: Define the model for the collection.

      The value of the modelName attribute is a String object used to identify the drag source for compatibility purposes.When you define the sources, the modelName attribute of the collectionDragSource tag must match this modelName or the discriminant attribute of the dataFlavor tag. In other words, this is an arbitrary name and works when the target and the source share the same modelName value or discriminant value.

  4. In the managed bean inserted into the EL expression in Step 2, implement the handler for the drop event.

    This method must take a DropEvent event as a parameter and return a DnDAction. The DndAction is the action that will be performed when the source is dropped. Valid return values are COPY, MOVE, and LINK, and are set when you define the actions attribute in Step 3. This method should use the dropEvent to get the transferable object, and from there, access the CollectionModel object in the dragged data and from there, access the actual data. The listener can then add that data to the model for the source and then return the DnDAction it performed: DnDAction.COPY, DnDAction.MOVE or DnDAction.LINK; otherwise, it should return DnDAction.NONE to indicate that the drop was rejected.

    Example 29-5 shows the event handler method on the CollectionDnd.java managed bean used in the collectionDropTarget demo that handles the copy of the row between the two tables shown in Figure 29-4.

    Example 29-5 Event Handler Code for a dropListener for a Collection

    public DnDAction (DropEvent dropEvent)
    {
      Transferable transferable = dropEvent.getTransferable();
      
      // The data in the transferable is the row key for the dragged component.
      DataFlavor<RowKeySet> rowKeySetFlavor =
                           DataFlavor.getDataFlavor(RowKeySet.class, "DnDDemoModel");
      RowKeySet rowKeySet = transferable.getData(rowKeySetFlavor);
      if (rowKeySet != null)
      {
        // Get the model for the dragged component.
        CollectionModel dragModel = transferable.getData(CollectionModel.class);
        if (dragModel != null)
        {
          // Set the row key for this model using the row key from the transferable.
          Object currKey = rowKeySet.iterator().next();
          dragModel.setRowKey(currKey);
            
          // And now get the actual data from the dragged model.
          // Note this won't work in a region.
          DnDDemoData dnDDemoData = (DnDDemoData)dragModel.getRowData();
          
          // Put the dragged data into the target model directly. 
          // Note that if you wanted validation/business rules on the drop,
          // this would be different.
          getTargetValues().add(dnDDemoData);
        }
        return DnDAction.COPY;
      }
      else
      {
        return DnDAction.NONE;      
      }
    }
      
    
  5. Add the collectionDragSource tag as a child to the component that will provide the source by dragging and dropping a Collection Drag Source from the Component Palette.

  6. In the Property Inspector, set the following values:

    • actions: This should be compatible with the action values defined in Step 3.

    • modelName: The modelName attribute is used to define the compatible collections that can be dropped. This must match the model name set in Step 3.

    • dragDropEndListener: This should be an expression that evaluates to a method on a managed bean that will do any clean up work necessary on the source collection. For more information, see Section 29.4.2, "What You May Need to Know About the dragDropEndListener".

29.4.2 What You May Need to Know About the dragDropEndListener

There may be cases when after a drop event, you have to clean up the source collection. For example, if the drag caused a move, you may have to clean up the source component.

The collectionDragSource tag contains the dragDropEndListener attribute that allows you to register a handler that contains logic for after the drag drop operation ends.

For example, if you allow a drag and drop to move an object, you may have to physically remove the object from the source component once you know the drop succeeded. Example 29-6 shows a handler for a dragDropEndListener. attribute

Example 29-6 Handler for dragDropEndListener

public void endListener(DropEvent dropEvent)
{
  Transferable transferable = dropEvent.getTransferable();
  
  // The data in the transferrable is the row key for the dragged component.
  DataFlavor<RowKeySet> rowKeySetFlavor =
      DataFlavor.getDataFlavor(RowKeySet.class, "DnDDemoModel");
  RowKeySet rowKeySet = transferable.getData(rowKeySetFlavor);
  if (rowKeySet != null)
  {
    Integer currKey = (Integer)rowKeySet.iterator().next();
    Object removed = getSource2Values().remove(currKey.intValue());
  }
  // Need to add the drag source table so it gets redrawn.
    AdfFacesContext.getCurrentInstance().addPartialTarget(dropEvent.getDragComponent());
  }

29.5 Adding Drag and Drop Functionality for Components

You can also allow components to be moved from one parent to another, or you can allow child components of a parent component to be reordered. For example, Figure 29-5 shows the darker panelBox component being moved from being the first child component of the panelGrid component to the last.

Figure 29-5 Drag and Drop Functionality Between Components

You can drag and drop components

29.5.1 How to Add Drag and Drop Functionality for Components

Adding drag and drop functionality for components is similar for objects. However, instead of using the attributeDragSource tag, use the componentDragSource tag. As with dragging and dropping objects or collections, you also must implement a dropListener handler.

To add drag and drop functionality:

  1. Add a dropTarget tag as a child to the target component, by dragging and dropping a Drop Target from the Component Palette.

  2. In the Insert Drop Target dialog, enter an expression that evaluates to a method on a managed bean that will handle the event (you will create this code in Step 4).

  3. With the dropTarget tag still selected, in the Property Inspector, select a valid action set for the action attribute.

  4. In the managed bean referenced in the EL expression created in Step 2 for the dropListener attribute, create the event handler method (using the same name as in the EL expression) that will handle the drag and drop functionality.

    This method must take a DropEvent event as a parameter and return a DnDAction, which is the action that will be performed when the source is dropped. Valid return values are DnDAction.COPY, DnDAction.MOVE, and DnDAction.LINK, and were set when you defined the target attribute in Step 2.

    This handler method should use the DropEvent event to get the transferable object and its data and then complete the move or copy, and reorder the components as needed. Once the method completes the drop, it should return the DnDAction it performed. Otherwise, it should return DnDAction.NONE to indicate that the drop was rejected.

    Example 29-7 shows the handleComponentMove event handler on the DemoDropHandler.java managed bean used by the componentDragSource JSF page in the demo application.

    Example 29-7 Event Handler Code for a dropListener That Handles a Component Move

    public DnDAction handleComponentMove(DropEvent dropEvent)
    {
      Transferable dropTransferable = dropEvent.getTransferable();
      UIComponent movedComponent =  dropTransferable.getData
                                   (DataFlavor.UICOMPONENT_FLAVOR);
      if ((movedComponent != null) &&
           DnDAction.MOVE.equals(dropEvent.getProposedAction()))
      {
        UIComponent dropComponent = dropEvent.getDropComponent();
        UIComponent dropParent = dropComponent.getParent();
        UIComponent movedParent = movedComponent.getParent();
        UIComponent rootParent;
        ComponentChange change;
        
        // Build the new list of IDs, placing the moved component after the dropped
        //component.
        String movedLayoutId = movedParent.getId();
        String dropLayoutId = dropComponent.getId();
          
        List<String> reorderedIdList = new
                                       ArrayList<String>(dropParent.getChildCount());
            
        for (UIComponent currChild : dropParent.getChildren())
        {
          String currId = currChild.getId();
            
          if (!currId.equals(movedLayoutId))
          {
            reorderedIdList.add(currId);
            if (currId.equals(dropLayoutId))
            {
              reorderedIdList.add(movedLayoutId);              
            }
          }
        }
          
        change = new ReorderChildrenComponentChange(reorderedIdList);
        rootParent = dropParent;
        // apply the change to the component tree immediately
        // change.changeComponent(rootParent);
          
        // redraw the shared parent
        AdfFacesContext.getCurrentInstance().addPartialTarget(rootParent);
        
        return DnDAction.MOVE;
      }
      else
      {
        return DnDAction.NONE;      
      }
    }
    
  5. Add a componentDragSource tag to the source component by dragging and dropping a Component Drag Source from the Component Palette as a child of the source component.