Oracle® Fusion Middleware Web User Interface Developer's Guide for Oracle Application Development Framework 11g Release 1 (11.1.1) Part Number B31973-03 |
|
|
View PDF |
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.
Note:
The ADF Facescalendar
component supports dragging and dropping activities to different areas on the calendar. For information about using drag and drop functionality in a calendar, see Section 15.5, "Adding Drag and Drop Functionality to a Calendar".This chapter includes the following sections:
Section 32.2, "Adding Drag and Drop Functionality for Attributes"
Section 32.3, "Adding Drag and Drop Functionality for Objects"
Section 32.4, "Adding Drag and Drop Functionality for Collections"
Section 32.5, "Adding Drag and Drop Functionality for Components"
You can configure components on a page so that the value of one component can be dragged and dropped onto another component. For example, as shown in Figure 32-1, you can configure a panelBox
component so that a user can drag and drop an outputText component onto its title. The result is that the panelBox's text
attribute gets its value from the value
attribute of the dragged outputText
component.
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 that component. To configure the source, you add the attributeDragSource
tag, also configured with the attribute providing the value.
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 32-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
.
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.
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:
In the Component Palette, expand the Operations section and drag and drop an Attribute Drop Target as a child to the target component.
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.
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.
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.
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 32-3.
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 32.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:
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.
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 5).
Tip:
You can also intercept the drop on the client by populating theclientDropListener
attribute. For more information, see Section 32.3.3, "What You May Need to Know About Using the ClientDropListener".In the Insert Data Flavor dialog, enter the class for the object that can be dropped onto the target, for example java.lang.Object
. This selection will be used to create a dataFlavor
tag, which 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.
Tip:
To specify a typed array in aDataFlavor
tag, add brackets ([]) to the class name, for example, java.lang.Object[]
.In the Structure window, select the dropTarget
tag. 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
.
Example 32-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
.
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 4. 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 defineddataFlavor
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 3.
Tip:
To specify a typed array in aDataFlavor
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 32-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 32-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; } }
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.
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 32-3 shows the code for an outputText
component that is the source of the drag and drop operation.
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 asdouble
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".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 32-4 shows a client handler that uses the logger to print a message.
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 32-4.
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 32.4.2, "What You May Need to Know About the dragDropEndListener".
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:
Add a collectionDropTarget
tag as a child to the target collection component by dragging a Collection Drop Target from the Component Palette.
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).
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.
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 32-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 32-4.
Example 32-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; } }
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.
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 32.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 so that the moved item is no longer part of the collection.
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 32-6 shows a handler for a dragDropEndListener
. attribute
Example 32-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()); }
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 32-5 shows the darker panelBox
component being moved from being the first child component of the panelGrid
component to the last.
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:
Add a dropTarget
tag as a child to the target component, by dragging and dropping a Drop Target from the Component Palette.
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).
With the dropTarget
tag still selected, in the Property Inspector, select a valid action set for the action
attribute.
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 32-7 shows the handleComponentMove
event handler on the DemoDropHandler.java
managed bean used by the componentDragSource
JSF page in the demo application.
Example 32-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; } }
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.